- 浏览: 220948 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
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 1255September 22, 2009 Posted by ... -
Spawning a Thread, the D way 8
2009-10-21 04:02 1725September 1, 2009 Posted by B ... -
The Anatomy of Reference Counting 7
2009-10-21 04:01 1140August 19, 2009 Posted by Bar ... -
On Actors and Casting 6
2009-10-21 03:59 1052July 16, 2009 Posted by Barto ... -
What’s Wrong with the Thread Object? 5
2009-10-21 03:58 1173July 7, 2009 Posted by Bartosz ... -
Multithreading Tutorial: Globals 4
2009-10-21 03:57 1253June 23, 2009 Posted by Barto ... -
Race-free Multithreading : Owner polymorphism 3
2009-10-21 03:56 831June 15, 2009 In my last post ... -
Race-free Multithreading 1
2009-10-21 03:41 1010Posted by Bartosz Milewski unde ... -
D语言并发编程特性前瞻
2008-08-19 19:49 1489http://wangyuanzju.blog.163.com ...
相关推荐
2. 实现Runnable接口: 如果你的类已经继承了其他类,不能直接继承Thread,可以选择实现Runnable接口。创建一个实现了Runnable接口的类,并实现run()方法。然后将Runnable对象传递给Thread构造函数,创建Thread实例...
`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` 运行环境: ...
npm install react-native-multithreading npx pod-install 需要包括的react-native-reanimated版本。 您可以自己打补丁,也可以等到它发布后再发布。 :warning: 警告:这仍然只是概念证明-请勿在生产中使用该库...
要么跑 php composer.phar require --prefer-dist grandmasterx/yii2-multithreading "*"或添加 "grandmasterx/yii2-multithreading": "*"到composer.json文件的 require 部分。用法安装扩展后,只需通过以下方式在...
2. **简化编程复杂度**:ThreadX提供了直观的API接口,使得开发者能够更加专注于业务逻辑的实现,而不必过分关注底层细节。 3. **提升系统稳定性**:基于ARM架构的稳定性和ThreadX的高可靠性,构建的系统能够承受更...
《.NET多线程编程》是一本专注于C# .NET平台下多线程技术的专业书籍。多线程是现代软件开发中的重要概念,特别是在多核处理器普及的今天,利用多线程可以有效提升应用程序的性能和响应性。本书旨在帮助读者理解和...
- Multithreading与GCD:了解多线程编程,特别是Grand Central Dispatch的使用。 4. **Xcode开发工具** - Interface Builder:用于创建用户界面的可视化编辑器。 - Debugger:掌握如何使用LLDB调试代码,定位和...
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
在项目的“Video-compression-multithreading-master”版本中,开发者已经实现了基本的多线程视频压缩框架,但还处于未完成状态。这意味着还有许多优化的空间,如错误处理、性能调优以及用户体验的改进。例如,可以...
- **第2章:线程管理**:深入探讨了如何有效地管理和控制线程。包括如何创建线程、如何控制线程的生命周期(如启动、暂停、停止等)、如何处理线程异常等问题。 - **第3章:线程间数据共享**:讨论了线程安全的数据...
多线程 leetcode hello-architect :pie::pie::pie:加餐加餐~ ...multithreading-topic : 多线程相关的练习 redis-jedis : redis 基本使用的练习 eshop-inventory : 构建基于 redis 的双写数据一致性问题的解决方案
java7 hashmap源码 ...-2.程序因IO堵塞时,cpu处于空闲状态(idle),可以释放cpu,让cpu为其他程序服务 -3.当系统有多个cpu时,可以为多个程序同时服务 ·cpu不再提高频率,而是提高核数: 由于受
2. **面向对象编程(OOP)** - 类与对象:定义类、创建对象,封装数据和行为。 - 继承:子类继承父类,实现代码复用和扩展。 - 多态:虚函数和纯虚函数实现接口多态,虚函数指针实现运行时多态。 - 封装:隐藏...
2. **第十四章:对话框(Dialog Box)编程** - 创建对话框资源:在Visual Studio中设计对话框布局。 - CDialog类:对话框的基本操作,如创建、显示和消息处理。 - 数据成员映射:用DDX(Dialog Data Exchange)和...
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 ...
2. **线程控制** - **sleep() 方法**:让当前线程暂时停止执行,进入阻塞状态,指定的时间后自动唤醒。 ```java try { Thread.sleep(1000); // 暂停1秒 } catch (InterruptedException e) { e.printStackTrace...
- 并发和多线程(Concurrency and Multithreading):如Task类和Mutex/Semaphore同步原语。 8. **学习资源** - 官方文档:微软提供了详尽的C#语言规范和.NET Framework文档。 - 开源项目:参与GitHub上的开源...
Java-Multithreading-Tutorial:“正版编码器” YouTube频道Java多线程教程的源代码
2015-2016并发和多线程分配的自述文件档案结构bin / ndfs脚本启动应用程序。 图包的build.xml ant脚本doc javadoc。 输入输入文件。 应用程序所需的lib jar文件。 README.txt此文件。 src此项目的源代码。...