执行中的线程顺序
Posted
技术标签:
【中文标题】执行中的线程顺序【英文标题】:Order of threads in execution 【发布时间】:2015-10-04 13:05:57 【问题描述】:考虑这个简单的并发示例:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
std::mutex mtx; // mutex for critical section
void print_block(int n, char c)
// critical section (exclusive access to std::cout signaled by locking mtx):
mtx.lock();
for (int i = 0; i<n; ++i) std::cout << c;
std::cout << '\n';
mtx.unlock();
int main()
std::thread th1(print_block, 50, '*');
std::thread th2(print_block, 50, '$');
th1.join();
th2.join();
return 0;
是否总是保证th1
将是第一个执行 for 循环的线程?
意思是,当我这样做时:
th1.join();
th2.join();
那我可以绝对确定th1
会先执行然后th2
吗?
【问题讨论】:
在处理并发编程时,永远不要对执行顺序做出任何假设。相反,假设所有情况都是可能的。这使您的代码更可靠,但矛盾的是,更简单。 You are usingstd::mutex
wrong, , 你永远不应该打电话给std::mutex::lock()
和std::mutex::unlock()
。而是在 print_block()
的开头创建一个 std::lock_guard<std::mutex>
并让它为您锁定和解锁互斥锁。
通常情况下,我们不关心谁先走,我们关心谁先完成,这就是为什么我们有函数join
。在您的示例中,th1
将首先出现,因为您在 main
中将 th1
放在 th2
之前。
使用条件变量或许能达到你想要的效果,但又有什么意义呢?
【参考方案1】:
不,您很可能会看到 th1
总是首先开始,因为该变量的线程构造首先完成(并且线程构造很昂贵),因此 th2
之后开始。这并不意味着有订单。
调用join()
与首先执行哪个线程没有任何关系,这是在您提供可调用对象时在构造时完成的。
th1
可以被构造然后被操作系统停止,这将导致th2
首先运行。除非您执行,否则没有顺序。
考虑这个例子,它为两个线程提供了一个更公平的开始,它有时会输出线程 1 作为第一个获得锁的人,有时会输出线程 2。
例子:
#include <iostream> // std::cout
#include <string> // std::string
#include <unordered_map> // std::unordered_map<K, V>
#include <thread> // std::thread
#include <mutex> // std::mutex
#include <atomic> // std::atomic<T>
std::unordered_map<std::thread::id, std::string> thread_map;
std::mutex mtx; // mutex for critical section
std::atomic<bool> go false ;
void print_block( int n, char c )
while ( !go ) // prevent threads from executing until go is set.
// critical section (exclusive access to std::cout signaled by locking mtx):
mtx.lock();
std::cout << thread_map.find( std::this_thread::get_id() )->second <<
" acquires the lock.\n";
mtx.unlock();
int main()
std::thread th1( print_block, 50, '*' );
std::thread th2( print_block, 50, '$' );
thread_map.emplace( std::make_pair( th1.get_id(), "Thread 1" ) );
thread_map.emplace( std::make_pair( th2.get_id(), "Thread 2" ) );
go.store( true );
th1.join();
th2.join();
return 0;
【讨论】:
但是在我的例子中,为什么顺序总是一样的?首先是th1
,然后是th2
?甚至当我用另一条替换线时,顺序保持不变。只有当我更换结构时,顺序才改变。
@ron 你什么时候替换了哪些行?如果您指的是对join()
的调用,那是因为join()
与线程开始执行的顺序无关。我解释了为什么首先构建一个线程通常会使其先于另一个开始。
好的,让我看看我是否明白了:两个线程都到达 while 循环 (while (!go)
),然后主线程在 go
标志上翻转,然后才有竞争条件获取锁?
@ron 获取锁没有竞争条件,锁是线程安全的。如果您的字面意思是两个线程将竞相获取锁,那么是的;这就是为什么它更公平。我们等到两个线程都完成了它们的构造,然后才让它们为锁而战。如果你执行我的例子,你会看到有时线程 2 在线程 1 之前获得了锁。以上是关于执行中的线程顺序的主要内容,如果未能解决你的问题,请参考以下文章