- 浏览: 215675 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
dysking:
SWT 和 JFace -
wangyuhfut:
东西不少啊。学习中。。。
一个比较好、中文说明的emacs配置文件 1 -
pacocai:
呵呵!学习,学习~~~不过要说编辑器的话个人更喜欢用VIM,比 ...
一个比较好、中文说明的emacs配置文件 1 -
zhf1zhf2:
这两百多个记起来也不容易啊
英国人是这样背的! -
regex:
试了两次,都是乱码,版本是23.1.1,看来不适合
汉化 Emacs 菜单
June 2, 2009
Since ownership plays a major role in race-free programming, it will be the first topic in my proposal for a race-free system. I presented the bird’s eye view of the system and provided a few teasers in my previous post. The design is based on published papers (see bibliography at the end). My contribution was to integrate several ideas into one package.
When I showed this proposal to my friends they either didn’t believe it could work or considered it too complex, depending which end they were looking at. From users’ perspective, the system looks relatively simple, so the natural reaction is: That can’t work. If you get into the details of why it works, and how the compiler knows you are in danger of a data race, you need some theory, and that is complex. So I decided to deal with some theory first, to show that the things work. If you’re not into theory, just look at the examples. They are usually simple to understand.
Owners
The ownership relationship is necessary to establish a tree-like structure among objects. This is needed by the compiler to decide which lock, if any, is responsible for the protection of each object, and take it when necessary. Simply speaking, the lock at the root of each tree protects the rest of the tree. If you think that your multithreaded programs don’t follow a tree structure, look at them closely. If they don’t, you either already have data races or are likely to develop them in the future.
-Every object has an owner
The owner may be another object–usually the embedding object. In the example below:
class Foo {
void doWork() { _bar.doWork(); }
private Bar _bar;
}
auto foo = new Foo;
the embedded object _bar is owned, at runtime, by the object foo (I repeat, the concrete object, not the class Foo). This is the default ownership relationship for embedded objects, so no special notation is needed to establish it (I’ll show later how to override this default).
There are also special symbolic “owners” that are used for the roots of ownership trees:
thread,
self,
unique, and
immutable.
unique and immutable are included in this list for convenience. I’ll discuss them later.
-Trees
Every object has just one owner for life, a condition necessary to create ownership trees that can be checked at compile time. Every tree has a single root and a lock is attached to that root, if needed.
The ownership information is embedded in the type of the object. Using this information, the compiler is able to deduce which lock must be held while accessing that object, and what kind of aliasing is allowed. All races (access to mutable shared variables without locking) are detected at compile time. I’ll sketch a proof later.
-What may be shared
Only immutable objects or objects rooted with a self-owned object may be shared between threads.
Additionally, objects whose direct owner is self (such objects are called monitors) may have multiple aliases while being shared. Monitors may own (and protect) other objects that are not monitors.
-Locking
The compiler will make sure that access to an object can only happen when the root of its ownership tree is locked (symbolic owners other than self are considered locked at all times). Since an object may only have one lock associated with it (at the top of its ownership tree), this condition is enough to ensure freedom from races.
Proof: I have to show that when a (mutable) object is seen by more than one thread, each access to it (read or write) is always protected by the same lock. Indeed, for an object to be shared between threads, the root of its ownership tree must be self, hence the top object must be a monitor. This monitor’s lock is always, automatically or explicitly, taken before accessing any member of the tree. The compiler knows which lock to take because the ownership information is encoded in the type of the object.
Introducing ownership annotations
Ownership is specified at the instance level (although it may be restricted at the class level). The previous example, which relied on default assignment of owners, is equivalent to the more explicit instance-level specification (that you will never see in actual programs):
Foo<owner::thread> foo = new Foo<owner::thread>;
This declares and constructs foo as being owned by the symbolic owner, thread. The embedded object _bar’s owner is foo.
-Creating a monitor
A self-owned object is a monitor (I will alternate between the notation using shared type modifier or explicit owner annotation, <owner::self>). It contains a hidden lock and its methods are, by default, synchronized. Continuing with my example:
auto fooMon = new shared Foo;
// The same as:
// auto fooMon = new Foo<owner::self>;
fooMon.doWork();
The variable fooMon is a monitor and the doWork method is implicitly synchronized. The object _bar is now owned by fooMon. Its type can be expressed (this is rarely needed, however see the example of external ownership) as:
Bar<owner::fooMon>
Types parameterized by runtime entities (fooMon is a runtime handle) are known in programming language theory as dependent types.
Notice that I’m using the same class to create thread-local and shared instances. This is usually possible unless there is a specific restriction at the class level.
Note to D programmers: The current semantics of D “shared” is slightly different from my proposal. For instance, it forces all embedded objects to be monitors (their methods must be synchronized by their own lock), requires explicit use of the synchronized keyword, and forces all access in non-synchronized methods to be sequentially consistent. (And it doesn’t guarantee freedom from races.)
Thread-local objects
The special thread owner, which is the owner of all thread-local objects, is conceptually always locked, so thread-local objects don’t require any synchronization. Also, thread is the default owner so, in the absence of any ownership annotations, all objects are thread-local. That’s one of the defaults that makes single-threaded programs work as-is.
Here’s an interesting twist–global and static objects are by default thread-local. This part has been implemented in D, uncovering a number of threading bugs in the process.
Monitors
The special self owner (or the shared type modifier) is used to create monitor objects. A monitor has a built-in lock and all its public methods are by default synchronized.
As always with defaults, the language must provide a (preferably safe) way to bypass them. To prevent locking, a method may be explicitly marked as lockfree. The compiler is obliged to check if the lockfree method doesn’t access the object’s members in a non-safe way (although it can’t prevent high-level races on lockfree variables). That restricts the lockfree constructs to those that don’t require whole-program analysis to prove their safety.
The lockfree annotation is essential for, among others, the double-checked locking pattern (DCLP). I showed its implementation as a teaser in my previous post.
Subobjects
As I explained earlier, data members of an object are by default owned by that object. This way they inherit the root owner from their parent. This is another default that makes single-threaded programs run without additional qualifiers.
Notice that there are two important aspects of ownership, the direct owner and the root owner, which might be different. The direct owner is used in type-checking, the root owner in deciding which synchronization method to use. Both are known or inferred during compilation.
As usual, the defaults may be overridden. For instance, you may embed a monitor in a thread-local object by qualifying it as self-owned/shared:
class Holder {
private Mon<owner::self> _mon;
}
or, in common notation, as shared:
class Holder {
private shared Mon _mon;
}
Here, _mon is not owned by Holder (the default has been overridden) so it doesn’t inherit its root owner. Its methods are synchronized by its own lock. As you can see, ownership tree not always reflects embedding. An embedded monitor starts a new tree.
Well, the situation is a bit subtler. Objects in Java or D have reference semantics, so there is a hidden pointer, or handle, in the code above. Accessing the handle is not the same as accessing the object proper. Consider this example:
class Holder {
private shared Mon _mon;
public setMon(shared Mon newMon) {
_mon = newMon;
}
}
Let’s instantiate a self-owned Holder and a self-owned Mon:
auto holder = new shared Holder;
auto newMon = new shared Mon;
holder.setMon(newMon);
Since holder is itself a monitor, the setMon method is automatically synchronized by its lock (it must be!). Therefore, strictly speaking, the handle part of _mon is owned by holderMon, whereas the object-proper part is self-owned.
You cannot embed a thread-owned object inside a monitor–the compiler would flag it as an error. This is part of alias control–a thread-local object might possibly have thread-local aliases that may be accessed without locking. Being part of a monitor, it could then migrate to another thread and cause a race.
What if a subobject is accessed directly (not through a method)? This may happen when the subobject is declared public:
class Foo {
public Bar _bar;
}
In that case not all uses of _bar are allowed. Consider this:
auto foo = new shared Foo;
foo._bar.m(); // error
Access to _bar must happen only when foo is locked. The compiler knows it because the full type of _bar is:
Bar<owner::foo>
Here’s the corrected code:
synchronized(foo) {
foo._bar.m();
}
An even better solution is to make _bar private and provide appropriate methods to access it. Those methods would be automatically synchronized for a shared foo.
unique and immutable
I discussed unique objects in one of my previous posts. Although not strictly required in the ownership scheme, uniqueness allows for very efficient and safe transmission of large objects between threads. It makes sense to include unique as another symbolic root owner, since its multithreaded semantics is different from other types and it doesn’t require locking.
Some languages, including D, define immutable objects, which cannot be modified after creation. Such objects may be freely shared and passed by reference between threads. Again, immutable may be used as a root owner.
Example
With the preliminaries out of the way, I can now explain in more detail the workings of the teaser from my previous post. Here’s the definition of the class MVar:
class MVar<T> {
private:
T _msg;
bool _full;
public:
void put(T msg) {
_msg := msg; // move
_full = true;
notify();
}
T take() {
while (!_full)
wait();
_full = false;
return := _msg;
}
}
First, let’s instantiate MVar as a shared (self-owned) monitor that is used to pass unique objects of class Foo as messages:
auto chanUnique = new shared MVar<unique Foo>;
The type of _msg in this instantiation is unique Foo, which is the same as Foo<owner::unique>. The method put takes unique Foo, so the following code is type-correct:
auto foo = new unique Foo;
chanUnique.put(:= foo); // move foo
Notice that unique objects cannot be assigned or passed by value–they have to be moved, hence the use of the move operator, :=. Internally, the method put also uses the move operator (good thinking on the part of the designer–otherwise MVar couldn’t be instantiated with unique). What’s interesting about this example is that messages are not deep-copied between threads. They are safely passed by reference.
Since chanUnique is self-owned (shared), both put and get are automatically synchronized.
Now let’s access chanUnique from another thread:
// another thread
unique Foo f2 = chanUnique.get(); // implicit move of rvalue
The return type of get is unique Foo, so the types check. I could have used the move operator, but since the right hand side is an rvalue, the compiler lets me use the assignment.
Now for the tricky case: What’s wrong with this code?
auto mVar = new shared MVar<Foo>;
auto myFoo = new Foo;
mVar.put(myFoo);
myFoo.unsyncMethod(); // ouch!
Since myFoo is created as thread-local (that’s the default), its methods are not synchronized. If I were able to pass it to shared MVar, another thread could obtain it through get. It could then call the unsynchronized method unsyncMethod at the moment when I was calling it. A data race would be possible! Or would it?
Guess what–the compiler won’t let you shoot yourself in the foot. It will notice that it would have to instantiate a shared object mVar with a thread-local member _msg. That’s against the rules! (A shared object cannot own a thread-local object.)
External ownership
In the original GRFJ paper the authors showed an example where one object was owned by another object without the former being embedded in the latter. They made an observation that, for the purpose of locking, the ownership relationship must be unchangeable: You can’t switch the owner on the fly. Therefore external ownership is allowed only if the owner is declared final.
final shared Lock lockObj = new shared Lock;
auto foo = new Foo<owner::lockObj>;
auto bar = new Bar<owner::lockObj>;
In this case, the compiler will only allow access to foo under the lock of lockObj, as in:
synchronized(lockObj) {
foo.method();
bar.method();
}
This construct is useful in situations where the locking discipline is not easily convertible to object hierarchy.
Conclusion
You might have noticed my use of dual notation. Most user code would be written with type qualifiers such as shared, unique, or immutable. However, in some cases I used an alternative notation that looked more like the specification of template parameters: <owner::self>, <owner::unique>, <owner::immutable>, or even <owner::thread> (in D they would be surrounded by !( and )). This was not meant to further confuse the reader, but as a gentle introduction to qualifier polymorphism, which I will describe in the next installment. I will show how classes and methods may be parameterized with different types of ownership, cutting down code duplication.
I’d like to thank Andrei Alexandrescu, Walter Bright, Sean Kelly and Jason House for very helpful comments. I’m also indebted to the D community for discussing my previous posts.
Bibliography
Boyapati, Rinard, A Parameterized Type System for Race-Free Java Programs
C. Flanagan, M. Abadi, Object Types against Races.
Since ownership plays a major role in race-free programming, it will be the first topic in my proposal for a race-free system. I presented the bird’s eye view of the system and provided a few teasers in my previous post. The design is based on published papers (see bibliography at the end). My contribution was to integrate several ideas into one package.
When I showed this proposal to my friends they either didn’t believe it could work or considered it too complex, depending which end they were looking at. From users’ perspective, the system looks relatively simple, so the natural reaction is: That can’t work. If you get into the details of why it works, and how the compiler knows you are in danger of a data race, you need some theory, and that is complex. So I decided to deal with some theory first, to show that the things work. If you’re not into theory, just look at the examples. They are usually simple to understand.
Owners
The ownership relationship is necessary to establish a tree-like structure among objects. This is needed by the compiler to decide which lock, if any, is responsible for the protection of each object, and take it when necessary. Simply speaking, the lock at the root of each tree protects the rest of the tree. If you think that your multithreaded programs don’t follow a tree structure, look at them closely. If they don’t, you either already have data races or are likely to develop them in the future.
-Every object has an owner
The owner may be another object–usually the embedding object. In the example below:
class Foo {
void doWork() { _bar.doWork(); }
private Bar _bar;
}
auto foo = new Foo;
the embedded object _bar is owned, at runtime, by the object foo (I repeat, the concrete object, not the class Foo). This is the default ownership relationship for embedded objects, so no special notation is needed to establish it (I’ll show later how to override this default).
There are also special symbolic “owners” that are used for the roots of ownership trees:
thread,
self,
unique, and
immutable.
unique and immutable are included in this list for convenience. I’ll discuss them later.
-Trees
Every object has just one owner for life, a condition necessary to create ownership trees that can be checked at compile time. Every tree has a single root and a lock is attached to that root, if needed.
The ownership information is embedded in the type of the object. Using this information, the compiler is able to deduce which lock must be held while accessing that object, and what kind of aliasing is allowed. All races (access to mutable shared variables without locking) are detected at compile time. I’ll sketch a proof later.
-What may be shared
Only immutable objects or objects rooted with a self-owned object may be shared between threads.
Additionally, objects whose direct owner is self (such objects are called monitors) may have multiple aliases while being shared. Monitors may own (and protect) other objects that are not monitors.
-Locking
The compiler will make sure that access to an object can only happen when the root of its ownership tree is locked (symbolic owners other than self are considered locked at all times). Since an object may only have one lock associated with it (at the top of its ownership tree), this condition is enough to ensure freedom from races.
Proof: I have to show that when a (mutable) object is seen by more than one thread, each access to it (read or write) is always protected by the same lock. Indeed, for an object to be shared between threads, the root of its ownership tree must be self, hence the top object must be a monitor. This monitor’s lock is always, automatically or explicitly, taken before accessing any member of the tree. The compiler knows which lock to take because the ownership information is encoded in the type of the object.
Introducing ownership annotations
Ownership is specified at the instance level (although it may be restricted at the class level). The previous example, which relied on default assignment of owners, is equivalent to the more explicit instance-level specification (that you will never see in actual programs):
Foo<owner::thread> foo = new Foo<owner::thread>;
This declares and constructs foo as being owned by the symbolic owner, thread. The embedded object _bar’s owner is foo.
-Creating a monitor
A self-owned object is a monitor (I will alternate between the notation using shared type modifier or explicit owner annotation, <owner::self>). It contains a hidden lock and its methods are, by default, synchronized. Continuing with my example:
auto fooMon = new shared Foo;
// The same as:
// auto fooMon = new Foo<owner::self>;
fooMon.doWork();
The variable fooMon is a monitor and the doWork method is implicitly synchronized. The object _bar is now owned by fooMon. Its type can be expressed (this is rarely needed, however see the example of external ownership) as:
Bar<owner::fooMon>
Types parameterized by runtime entities (fooMon is a runtime handle) are known in programming language theory as dependent types.
Notice that I’m using the same class to create thread-local and shared instances. This is usually possible unless there is a specific restriction at the class level.
Note to D programmers: The current semantics of D “shared” is slightly different from my proposal. For instance, it forces all embedded objects to be monitors (their methods must be synchronized by their own lock), requires explicit use of the synchronized keyword, and forces all access in non-synchronized methods to be sequentially consistent. (And it doesn’t guarantee freedom from races.)
Thread-local objects
The special thread owner, which is the owner of all thread-local objects, is conceptually always locked, so thread-local objects don’t require any synchronization. Also, thread is the default owner so, in the absence of any ownership annotations, all objects are thread-local. That’s one of the defaults that makes single-threaded programs work as-is.
Here’s an interesting twist–global and static objects are by default thread-local. This part has been implemented in D, uncovering a number of threading bugs in the process.
Monitors
The special self owner (or the shared type modifier) is used to create monitor objects. A monitor has a built-in lock and all its public methods are by default synchronized.
As always with defaults, the language must provide a (preferably safe) way to bypass them. To prevent locking, a method may be explicitly marked as lockfree. The compiler is obliged to check if the lockfree method doesn’t access the object’s members in a non-safe way (although it can’t prevent high-level races on lockfree variables). That restricts the lockfree constructs to those that don’t require whole-program analysis to prove their safety.
The lockfree annotation is essential for, among others, the double-checked locking pattern (DCLP). I showed its implementation as a teaser in my previous post.
Subobjects
As I explained earlier, data members of an object are by default owned by that object. This way they inherit the root owner from their parent. This is another default that makes single-threaded programs run without additional qualifiers.
Notice that there are two important aspects of ownership, the direct owner and the root owner, which might be different. The direct owner is used in type-checking, the root owner in deciding which synchronization method to use. Both are known or inferred during compilation.
As usual, the defaults may be overridden. For instance, you may embed a monitor in a thread-local object by qualifying it as self-owned/shared:
class Holder {
private Mon<owner::self> _mon;
}
or, in common notation, as shared:
class Holder {
private shared Mon _mon;
}
Here, _mon is not owned by Holder (the default has been overridden) so it doesn’t inherit its root owner. Its methods are synchronized by its own lock. As you can see, ownership tree not always reflects embedding. An embedded monitor starts a new tree.
Well, the situation is a bit subtler. Objects in Java or D have reference semantics, so there is a hidden pointer, or handle, in the code above. Accessing the handle is not the same as accessing the object proper. Consider this example:
class Holder {
private shared Mon _mon;
public setMon(shared Mon newMon) {
_mon = newMon;
}
}
Let’s instantiate a self-owned Holder and a self-owned Mon:
auto holder = new shared Holder;
auto newMon = new shared Mon;
holder.setMon(newMon);
Since holder is itself a monitor, the setMon method is automatically synchronized by its lock (it must be!). Therefore, strictly speaking, the handle part of _mon is owned by holderMon, whereas the object-proper part is self-owned.
You cannot embed a thread-owned object inside a monitor–the compiler would flag it as an error. This is part of alias control–a thread-local object might possibly have thread-local aliases that may be accessed without locking. Being part of a monitor, it could then migrate to another thread and cause a race.
What if a subobject is accessed directly (not through a method)? This may happen when the subobject is declared public:
class Foo {
public Bar _bar;
}
In that case not all uses of _bar are allowed. Consider this:
auto foo = new shared Foo;
foo._bar.m(); // error
Access to _bar must happen only when foo is locked. The compiler knows it because the full type of _bar is:
Bar<owner::foo>
Here’s the corrected code:
synchronized(foo) {
foo._bar.m();
}
An even better solution is to make _bar private and provide appropriate methods to access it. Those methods would be automatically synchronized for a shared foo.
unique and immutable
I discussed unique objects in one of my previous posts. Although not strictly required in the ownership scheme, uniqueness allows for very efficient and safe transmission of large objects between threads. It makes sense to include unique as another symbolic root owner, since its multithreaded semantics is different from other types and it doesn’t require locking.
Some languages, including D, define immutable objects, which cannot be modified after creation. Such objects may be freely shared and passed by reference between threads. Again, immutable may be used as a root owner.
Example
With the preliminaries out of the way, I can now explain in more detail the workings of the teaser from my previous post. Here’s the definition of the class MVar:
class MVar<T> {
private:
T _msg;
bool _full;
public:
void put(T msg) {
_msg := msg; // move
_full = true;
notify();
}
T take() {
while (!_full)
wait();
_full = false;
return := _msg;
}
}
First, let’s instantiate MVar as a shared (self-owned) monitor that is used to pass unique objects of class Foo as messages:
auto chanUnique = new shared MVar<unique Foo>;
The type of _msg in this instantiation is unique Foo, which is the same as Foo<owner::unique>. The method put takes unique Foo, so the following code is type-correct:
auto foo = new unique Foo;
chanUnique.put(:= foo); // move foo
Notice that unique objects cannot be assigned or passed by value–they have to be moved, hence the use of the move operator, :=. Internally, the method put also uses the move operator (good thinking on the part of the designer–otherwise MVar couldn’t be instantiated with unique). What’s interesting about this example is that messages are not deep-copied between threads. They are safely passed by reference.
Since chanUnique is self-owned (shared), both put and get are automatically synchronized.
Now let’s access chanUnique from another thread:
// another thread
unique Foo f2 = chanUnique.get(); // implicit move of rvalue
The return type of get is unique Foo, so the types check. I could have used the move operator, but since the right hand side is an rvalue, the compiler lets me use the assignment.
Now for the tricky case: What’s wrong with this code?
auto mVar = new shared MVar<Foo>;
auto myFoo = new Foo;
mVar.put(myFoo);
myFoo.unsyncMethod(); // ouch!
Since myFoo is created as thread-local (that’s the default), its methods are not synchronized. If I were able to pass it to shared MVar, another thread could obtain it through get. It could then call the unsynchronized method unsyncMethod at the moment when I was calling it. A data race would be possible! Or would it?
Guess what–the compiler won’t let you shoot yourself in the foot. It will notice that it would have to instantiate a shared object mVar with a thread-local member _msg. That’s against the rules! (A shared object cannot own a thread-local object.)
External ownership
In the original GRFJ paper the authors showed an example where one object was owned by another object without the former being embedded in the latter. They made an observation that, for the purpose of locking, the ownership relationship must be unchangeable: You can’t switch the owner on the fly. Therefore external ownership is allowed only if the owner is declared final.
final shared Lock lockObj = new shared Lock;
auto foo = new Foo<owner::lockObj>;
auto bar = new Bar<owner::lockObj>;
In this case, the compiler will only allow access to foo under the lock of lockObj, as in:
synchronized(lockObj) {
foo.method();
bar.method();
}
This construct is useful in situations where the locking discipline is not easily convertible to object hierarchy.
Conclusion
You might have noticed my use of dual notation. Most user code would be written with type qualifiers such as shared, unique, or immutable. However, in some cases I used an alternative notation that looked more like the specification of template parameters: <owner::self>, <owner::unique>, <owner::immutable>, or even <owner::thread> (in D they would be surrounded by !( and )). This was not meant to further confuse the reader, but as a gentle introduction to qualifier polymorphism, which I will describe in the next installment. I will show how classes and methods may be parameterized with different types of ownership, cutting down code duplication.
I’d like to thank Andrei Alexandrescu, Walter Bright, Sean Kelly and Jason House for very helpful comments. I’m also indebted to the D community for discussing my previous posts.
Bibliography
Boyapati, Rinard, A Parameterized Type System for Race-Free Java Programs
C. Flanagan, M. Abadi, Object Types against Races.
发表评论
-
Ownership Systems against Data Races 10
2009-10-21 04:04 1212September 22, 2009 Posted by ... -
Spawning a Thread, the D way 8
2009-10-21 04:02 1668September 1, 2009 Posted by B ... -
The Anatomy of Reference Counting 7
2009-10-21 04:01 1097August 19, 2009 Posted by Bar ... -
On Actors and Casting 6
2009-10-21 03:59 1010July 16, 2009 Posted by Barto ... -
What’s Wrong with the Thread Object? 5
2009-10-21 03:58 1130July 7, 2009 Posted by Bartosz ... -
Multithreading Tutorial: Globals 4
2009-10-21 03:57 1209June 23, 2009 Posted by Barto ... -
Race-free Multithreading : Owner polymorphism 3
2009-10-21 03:56 783June 15, 2009 In my last post ... -
Race-free Multithreading 1
2009-10-21 03:41 977Posted by Bartosz Milewski unde ... -
D语言并发编程特性前瞻
2008-08-19 19:49 1456http://wangyuanzju.blog.163.com ...
相关推荐
Java--多线程Java-多线程
Real-Time EmbeddedMultithreading:Using ThreadX and ARM
npm install react-native-multithreading npx pod-install 需要包括的react-native-reanimated版本。 您可以自己打补丁,也可以等到它发布后再发布。 :warning: 警告:这仍然只是概念证明-请勿在生产中使用该库...
`python CVE-2018-2628-MultiThreading.py` Usage: Place the 'ip:port' to be detected into the url.txt file in the same directory. Then run: `python CVE-2018-2628-MultiThreading.py` 运行环境: ...
要么跑 php composer.phar require --prefer-dist grandmasterx/yii2-multithreading "*"或添加 "grandmasterx/yii2-multithreading": "*"到composer.json文件的 require 部分。用法安装扩展后,只需通过以下方式在...
c#.net 多线程编程 <br>I share only those books I have read and considered classic.
实时操作系统 Treadx 详解,原始英文版,详细介绍了 Treadx 的各个接口以及使用;
1,Real-Time Embedded Multithreading Using ThreadX and MIPS 2,Real-Time_Embedded_Multithreading_Using_ThreadX 3,Real-Time Embedded ...5,(CMP) Real-Time Embedded Multithreading--Using ThreadX & ARM
多线程 leetcode hello-architect :pie::pie::pie:加餐加餐~ ...multithreading-topic : 多线程相关的练习 redis-jedis : redis 基本使用的练习 eshop-inventory : 构建基于 redis 的双写数据一致性问题的解决方案
视频压缩多线程适用于我的UROP的Android应用程序要以多线程方式压缩视频-通过牺牲拆分,合并和转换时间,可以大大节省压缩时间。 该版本是未完成的版本。
java7 hashmap源码 ...-2.程序因IO堵塞时,cpu处于空闲状态(idle),可以释放cpu,让cpu为其他程序服务 -3.当系统有多个cpu时,可以为多个程序同时服务 ·cpu不再提高频率,而是提高核数: 由于受
mastering-c-multithreading-write-robust-concurrent-and-parallel-applications_compress
2 ■ Managing threads 3 ■ Sharing data between threads 4 ■ Synchronizing concurrent operations 5 ■ The C++ memory model and operations on atomic types 6 ■ Designing lock-based concurrent data ...
java-多线程java-多线程用于多线程的 Java 并发 API。
Java-Multithreading-Tutorial:“正版编码器” YouTube频道Java多线程教程的源代码
2015-2016并发和多线程分配的自述文件档案结构bin / ndfs脚本启动应用程序。 图包的build.xml ant脚本doc javadoc。 输入输入文件。 应用程序所需的lib jar文件。 README.txt此文件。 src此项目的源代码。...
2 Theoretical Background 3 Modern Architectures 4C++11 Multithreading 5 Advanced C++11 Multithreading 6OpenMP 7 Compute Unified Device Architecture 8 Advanced CUDA Programming 9 Message Passing ...
ndt_omp 该软件包提供了从pcl派生的OpenMP增强的正态分布变换(和GICP)算法。 将NDT算法修改为对SSE友好并且是多线程的。 它的运行速度比pcl中的原始版本快10倍。 基准测试(在Core i7-6700K上) ...
LRU多线程 LRU的Java实现
第一个示例是概念证明,表明我们可以触发 2 个线程并让它们以不同的顺序完成。 seconf 示例表明,我们可以通过在单独的线程上拆分逻辑任务来加速一批愚蠢的长 3 路数组副本。 这是我的第一个多线程应用程序,我想...