多线程初学者问题(C++)

3

我正在尝试学习C++中的多线程。我试图将向量的元素作为参数传递给pthread_create。但是,它并没有按预期工作。

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <vector>
using namespace std;
void *count(void *arg)
{
    int threadId = *((int *)arg);
    cout << "Currently thread with id " << threadId << " is executing " << endl;
    pthread_exit(NULL);
}
int main()
{
    pthread_t thread1;
    vector<int> threadId(2);
    threadId[0] = 99;
    threadId[1] = 100;
    int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]);
    if (retVal)
    {
        cout << "Error in creating thread with Id: " << threadId[0] << endl;
        exit(-1);
    }
    pthread_t thread2;
    retVal = pthread_create(&thread2, NULL, count, (void *)&threadId[1]);
    if (retVal)
    {
        cout << "Error in creating thread with Id: " << threadId[1] << endl;
        exit(-1);
    }
    pthread_exit(NULL);
}

我得到的输出是:

当前正在执行线程 ID 为 99。
当前正在执行线程 ID 为 0。

然而,根据我的理解,它应该是:

当前正在执行线程 ID 为 99。
当前正在执行线程 ID 为 100。

这里有什么我忽略的吗?


3
你不使用 std::thread 是否有特别的原因?它的接口更好。 - HolyBlackCat
2
首先,不要使用pthread。从C++11开始就有std::thread(或更好的std::async/std::future)。其他非常有用的学习内容包括lambda函数(及其捕获行为)、std::shared_ptr(用于在线程之间共享对象)和std::mutex(以及std::unique_lock/std::scoped_lock)来保护您的数据。关于您的问题,线程执行顺序不取决于您,而是操作系统的线程调度程序决定。 - Pepijn Kramer
2
@HolyBlackCat 我认为std::async是更好的抽象。返回的future允许在需要时几乎无痛同步“调用”线程。换句话说,std::thread更多地是“如何”,而std::async更多地是“什么”。 - Pepijn Kramer
2
在离开主函数之前,你应该加入线程,否则向量上会出现竞争条件(在主函数结束时被销毁)。 - Mat
2
@Mat 注意,由std::async返回的std::future的析构函数将执行同步操作。只需确保在futures之前创建向量即可。这是我喜欢std::async的另一个原因之一。 - Pepijn Kramer
1个回答

2
int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]);

你完全没有任何保证,新的执行线程现在正在运行,没有任何延迟。

pthread_create 保证的仅仅是线程函数 thread1 将在某个时刻开始执行。它可能在 pthread_create() 返回之前就开始执行,也可能在之后的某个时刻开始执行。新的执行线程何时开始执行真的是一个大谜团,但你可以放心,新的执行线程最终会开始执行。

对于第二个执行线程也是一样。

因此,两个执行线程都有可能在你的 main() 返回之后才开始运行,并且在你的向量被销毁之后。在所示代码中没有任何保证执行线程将在向量(以所示方式传递其中的内容)被销毁之前执行。这会导致未定义的行为。

你需要使用其他与线程相关的设施,以便正确同步多个执行线程。此外,你正在使用较旧的 POSIX 线程。现代 C++ 使用 std::thread,它们比其前身提供了许多优势,完全类型安全(没有丑陋的转换),并具有防止常见编程错误的多个属性(然而,在这种情况下,std::thread 也没有同步保证,这通常是所有典型执行线程实现的情况)。


2
值得一提的是,针对 main() 在子线程执行完成之前返回的 pthreads 修复方法是在 main() 结束时(或接近结束时)调用 pthread_join() 来等待每个子线程的执行。 - Jeremy Friesner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接