执行中的线程顺序

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 using std::mutex wrong, , 你永远不应该打电话给std::mutex::lock()std::mutex::unlock()。而是在 print_block() 的开头创建一个 std::lock_guard&lt;std::mutex&gt; 并让它为您锁定和解锁互斥锁。 通常情况下,我们不关心谁先走,我们关心谁先完成,这就是为什么我们有函数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 之前获得了锁。

以上是关于执行中的线程顺序的主要内容,如果未能解决你的问题,请参考以下文章

Jmeter--多个线程组顺序执行和并行执行

执行中的线程顺序

Jmeter -- 循环控制器和线程并发(关注执行顺序)

如何顺序执行ExecutorService中的任务?

面试官:Java中线程是按什么顺序执行的?

Java--线程的先后执行顺序控制