首先,我认为重要的是要说我是多线程新手并且对此知之甚少。我试图用线程在C ++中编写一些程序并遇到一个问题(问题),我现在会向你解释:
我想用几个线程来填充数组,这是我的代码:
static const int num_threads = 5;
int A[50], n;
//------------------------------------------------------------
void ThreadFunc(int tid)
{
for (int q = 0; q < 5; q++)
{
A[n] = tid;
n++;
}
}
//------------------------------------------------------------
int main()
{
thread t[num_threads];
n = 0;
for (int i = 0; i < num_threads; i++)
{
t[i] = thread(ThreadFunc, i);
}
for (int i = 0; i < num_threads; i++)
{
t[i].join();
}
for (int i = 0; i < n; i++)
cout << A[i] << endl;
return 0;
}
由于这个计划,我得到: 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 等等。
据我所知,第二个线程只在第一个线程完成将所有元素写入数组时才开始将元素写入数组。 问题是为什么线程不能同时工作?我的意思是为什么我不能得到类似的东西:
0 1 2 0 3 1 4 等等。
有什么方法可以解决这个问题吗?
提前谢谢。
答案 0 :(得分:3)
由于从多个线程访问n
,因此需要同步这些访问,以便在一个线程中进行的更改不会与另一个线程中所做的更改发生冲突。有(至少)两种方法可以做到这一点。
首先,您可以使n
成为原子变量。只需更改其定义,并使用值的增量:
std::atomic<int> n;
...
A[n++] = tid;
或者您可以将所有访问权限包含在关键部分中:
std::mutex mtx;
int next_n() {
std::unique_lock<std::mutex> lock(mtx);
return n++;
}
在每个线程中,不要直接递增n
,而是调用该函数:
A[next_n()] = tid;
这比原子访问要慢得多,所以这里不合适。在更复杂的情况下,这将是正确的解决方案。
答案 1 :(得分:0)
工作者功能如此之短,即完成执行如此之快,以至于每个线程在下一个线程开始之前完成的可能性。此外,您可能需要链接线程库以获取真正的线程,例如-lpthread
。即便如此,您获得的结果纯粹是偶然的,并且可以按任何顺序出现。
要使程序正确同步,需要进行两处更正。变化:
int n;
// ...
A[n] = tid; n++;
到
std::atomic_int n;
// ...
A[n++] = tid;
通常最好完全避免同步问题并跨线程分割工作负载。由于每次迭代完成的工作在这里是相同的,因此它与均匀分割工作一样简单:
void ThreadFunc(int tid, int first, int last)
{
for (int i = first; i < last; i++)
A[i] = tid;
}
在main中,修改线程创建循环:
for (int first = 0, i = 0; i < num_threads; i++) {
// possible num_threads does not evenly divide ASIZE.
int last = (i != num_threads-1) ? std::size(A)/num_threads*(i+1) : std::size(A);
t[i] = thread(ThreadFunc, i, first, last);
first = last;
}
当然,通过这样做,即使数组可能无序写入,每次都会将值存储到相同的位置。