C++ 执行中线程的顺序

C++ 执行中线程的顺序,c++,multithreading,visual-studio,visual-studio-2012,C++,Multithreading,Visual Studio,Visual Studio 2012,考虑这个简单的并发示例: #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) { //

考虑这个简单的并发示例:

#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
,然后执行
th2

不,您很可能会看到
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;
}
#包括//std::cout
#include//std::string
#include//std::无序映射
#include//std::thread
#include//std::mutex
#include//std::atomic
std::无序映射线程映射;
std::mutex mtx;//临界区互斥
std::原子go{false};
无效打印块(整数n,字符c)
{
而(!go){}//则在设置go之前阻止线程执行。
//关键部分(通过锁定mtx发出信号的对std::cout的独占访问):
mtx.lock();

std::cout second但在我的例子中,为什么顺序总是一样的?首先是
th1
,然后是
th2
?即使我用一行替换另一行,顺序也保持不变。只有当我替换了结构时,顺序才改变。@ron当你用另一行替换哪一行时?如果你是指对
连接的调用()
,这是因为
join()
与线程开始执行的顺序无关。我解释了为什么先构造一个线程通常会使它先于另一个线程启动。好的,让我看看我是否理解了:两个线程都到达while循环(
while(!go){}
)然后主线程在
go
标志上翻转,然后才有获取锁的竞态条件?@ron获取锁没有竞态条件,锁是线程安全的。如果你字面上的意思是两个线程都将竞态获取锁,那么是的;这就是为什么它更公平。我们等到两个线程都完成了eir构造,然后我们才让他们为锁而战。如果你执行我的示例,你会看到有时线程2在线程1之前获得锁。在处理并发编程时,不要对执行顺序做任何假设。相反,假设所有情况都是可能的。这使你的代码更可靠矛盾的是,更简单的是,您不应该调用
std::mutex::lock()
std::mutex::unlock()
。而应该在
print\u block()的开头创建一个
std::lock\u guard
并让它为您锁定和解锁互斥锁。通常,我们不关心谁先走,我们关心谁先完成,这就是为什么我们有函数
join
。在您的示例中,
th1
将先走,因为您在
main
中将
th1
放在
th2
之前。使用条件变量可以实现至少你想做,但有什么意义?
#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;
}