C++ 增加计数器时避免争用条件
我以前也在这个话题上发表过文章,但到目前为止我还没有多少运气。我把它归结为我的一个坏问题。这一次,我制作了一个简短的可编译示例,显示了我试图避免的不良行为。我希望大家对此表示感谢 问题是两个(或更多)线程被设置为运行同一进程,并且它们的“id”决定了它们对变量数据的哪一部分进行操作。当前,两个线程都将更新计数器 电流输出如下所示C++ 增加计数器时避免争用条件,c++,multithreading,boost,boost-thread,C++,Multithreading,Boost,Boost Thread,我以前也在这个话题上发表过文章,但到目前为止我还没有多少运气。我把它归结为我的一个坏问题。这一次,我制作了一个简短的可编译示例,显示了我试图避免的不良行为。我希望大家对此表示感谢 问题是两个(或更多)线程被设置为运行同一进程,并且它们的“id”决定了它们对变量数据的哪一部分进行操作。当前,两个线程都将更新计数器 电流输出如下所示 tid = 0, var[tid] = 0 tid = 0, var[tid] = 1 tid = 0, var[tid] = 2 tid = 0, var[tid]
tid = 0, var[tid] = 0
tid = 0, var[tid] = 1
tid = 0, var[tid] = 2
tid = 0, var[tid] = 3
tid = 0, var[tid] = 4
tid = 0, var[tid] = 5
tid = 0, var[tid] = 6
tid = 0, var[tid] = 7
tid = 0, var[tid] = 8
tid = 0, var[tid] = 9
tid = 1, var[tid] = 0
Press any key to continue . . .
期望的输出应该是这样的
tid = 0, var[tid] = 0
tid = 1, var[tid] = 0
tid = 0, var[tid] = 1
tid = 1, var[tid] = 1
tid = 0, var[tid] = 2
tid = 1, var[tid] = 2
tid = 0, var[tid] = 3
tid = 1, var[tid] = 3 etc.
如蒙指导,不胜感激
编辑:我用预期的代码更新了答案
[注意这里效率很重要,我希望尽快完成流程]
#include <iostream>
#include <boost/thread.hpp>
int var[2];
int mT;
int mTotalSamples;
boost::mutex mCountMutex;
boost::thread *threadMap[2];
using namespace std;
void process()
{
int tid = 1;
// sleep for 1 seconds - just to make sure threadMap
// has been assigned (only ncessary for this demo).
boost::this_thread::sleep(boost::posix_time::seconds(1));
if (threadMap[0]->get_id() == boost::this_thread::get_id()){ tid = 0;}
while ( mT < mTotalSamples )
{
// perform processing
var[tid] = mT;
// processing complete
mCountMutex.lock(); // (a thread waits to aquire mutex)
cout << "tid = " << tid << ", var[tid] = " << var[tid] << endl;
mT++; // How to stop both threads incrementing this?
mCountMutex.unlock();
}
}
int main()
{
boost::thread_group threads;
mT = 0;
mTotalSamples = 10;
threadMap[0] = threads.create_thread( boost::bind(&process) );
threadMap[1] = threads.create_thread( boost::bind(&process) );
threads.join_all();
return 0;
}
#包括
#包括
int-var[2];
int mT;
int mTotalSamples;
mutex-mCountMutex;
boost::thread*threadMap[2];
使用名称空间std;
无效过程()
{
int-tid=1;
//睡眠1秒钟-只是为了确保threadMap
//已分配(仅此演示不需要)。
boost::this_线程::sleep(boost::posix_时间::秒(1));
if(threadMap[0]->get_id()==boost::this_thread::get_id()){tid=0;}
而(mT<总样本数)
{
//执行处理
var[tid]=mT;
//处理完成
mCountMutex.lock();/(线程等待获取互斥)
cout从您的预期输出判断,您希望您的线程在每次更新后进行同步。boost
库提供了这样的功能,如果您在进程的while循环开始或结束时为它设置了一个wait
,应该可以实现这一功能
#include <iostream>
#include <boost/thread.hpp>
int var[2];
int mT;
int mTotalSamples;
boost::mutex mCountMutex;
boost::thread *threadMap[2];
boost::barrier bar(2);
using namespace std;
void process()
{
int tid = 1;
// sleep for 2 seconds - just to make sure threadMap
// has been assigned (only ncessary for this demo).
boost::this_thread::sleep(boost::posix_time::seconds(2));
if (threadMap[0]->get_id() == boost::this_thread::get_id()){ tid = 0;}
while ( mT < mTotalSamples )
{
// perform processing
var[tid] = mT;
// processing complete
bar.wait();
if (threadMap[0]->get_id() == boost::this_thread::get_id())
{
mT++;
cout << "var[0] = " << var[0] << endl;
cout << "var[1] = " << var[1] << endl;
}
bar.wait();
}
}
int main()
{
boost::thread_group threads;
mT = 0;
mTotalSamples = 10;
threadMap[0] = threads.create_thread( boost::bind(&process) );
threadMap[1] = threads.create_thread( boost::bind(&process) );
threads.join_all();
return 0;
}
#包括
#包括
int-var[2];
int mT;
int mTotalSamples;
mutex-mCountMutex;
boost::thread*threadMap[2];
增压:隔离栅(2);
使用名称空间std;
无效过程()
{
int-tid=1;
//睡眠2秒钟-只是为了确保threadMap
//已分配(仅此演示不需要)。
boost::this_线程::sleep(boost::posix_时间::秒(2));
if(threadMap[0]->get_id()==boost::this_thread::get_id()){tid=0;}
而(mT<总样本数)
{
//执行处理
var[tid]=mT;
//处理完成
等等();
if(线程映射[0]->get_id()==boost::this_thread::get_id())
{
mT++;
不能将此int mT;
local-in-process()设置为全局。
或者您需要一个int mT[2];
那么您就不需要互斥锁了。听起来您希望线程能够交错访问互斥锁。但是,您的代码中没有强制执行互斥锁的内容。如果您希望操作以特定的顺序开始,那么线程可能不是解决方案…@OliCharlesworth互斥锁不是问题,每个线程应该只执行p当计数器增加一次时,执行行,var[tid]=mT;。但是,老实说,我认为使用cout可能会混淆问题。仅为了保护计数器,可以使用联锁操作,如LONG\u cdecl InterlockedIncrement(LONG volatile*加数);
在windows上。您真的需要保护您的var
变量吗?尝试调度两个线程以相互协作没有多大意义,您也可以忘记线程,只需按程序/手动调度操作。线程在切换到另一个线程之前会有一定的执行时间在那个时候,线程可以完成比你想要的更多的工作,并且迫使一个线程向另一个线程让步可能会影响调度器的效率,从而影响应用程序的整体性能。你应该真正考虑线程是否是为你的目的所必需的。it@dreamlax感谢s、 我知道这个例子有点做作,因为我已经应用了简化,使问题易于理解。事实上,一个线程的一个循环可能需要几秒钟,在实践中,我会执行尽可能多的线程作为核心,并划分“var”的更新在这些线程之间平均分配,从而减少完成一个循环的时间。线程绝对适用于此目的。感谢您的评论,尽管添加第二个计数器无助于满足我的需要。同样,这只是因为我将我的问题简化为一个更简单的问题,为了使问题更具可读性,上下文会丢失一些什么t、 @AlexS:好的,我已经批准了你的编辑,谢谢!顺便说一句,在你的应用程序中,有必要对bar.wait()
进行两次调用吗?我原以为在循环的顶部只调用一次就足够了。嗯,我想是的,但我可能错了。如果没有它,我认为线程2有可能完成,var[tid]=mT;在线程1管理递增mT之前。第二个屏障确保这绝对不会发生。@Alex:你能验证一下吗?诸如屏障之类的同步点会显著降低并行性能,因此你应该尽量少用它们!我认为有必要确定两个,但只有当t处理量增加到实际大小。即,如果var[0]表示500万次操作,而var[1]表示2000万个操作,那么线程1将在线程2完成其工作之前增加计数器。@Alex:好的,但是如果在循环的底部(即计数器增加之后)设置屏障就足够了吗?