1.并发冲突实例
struct IDCreator
{
static IDCreator& Instance()
{
static IDCreator instance;
return instance;
}
int GetNewID()
{
return ++ _id;
}
private:
IDCreator()
: _id(0)
{
}
int _id;
};
IDCreator(ID创建器)设计为单例类,每次调用它的GetNewID()方法,都会先自增“id”然后返回。先在单线程环境下测试:
#include <iostream>
///使用SET容器,在添加新ID前检查该ID是否已存在
#include <set>
using namespace std;
...
///写个辅助自由函数,方便使用
int get_new_user_id()
{
return IDCreator::Instance().GetNewID();
}
int test()
{
set <int> ids;
for(int i = 0; i < 10000; ++i)
{
int id = get_new_user_id();
if(ids.find(id) != ids.end())
{
cout << "该ID已经存在了:" << id << "。" << endl;
continue;
}
ids.insert(id);
}
}
int main()
{
test();
return 0;
}
毫无问题,ID一个也不重复。
多线程(异步)下的测试函数test()实现以及需要增加的头文件,代码如下:
#include <iostream>
#include <set> ///使用SET容器,在添加新ID前检查该ID是否已存在
#include <vector>
#include <future>
#include <fstream>
using namespace std;
struct IDCreator
{
static IDCreator& Instance()
{
static IDCreator instance;
return instance;
}
int GetNewID()
{
return ++ _id;
}
private:
IDCreator()
: _id(0)
{
}
int _id;
};
///写个辅助自由函数,方便使用
int get_new_user_id()
{
return IDCreator::Instance().GetNewID();
}
//test修改如下
int test()
{
typedef future <int> IntFuture;
vector <IntFuture> futures;
for(int i = 0; i < 1000; ++i)
{
//1000个异步执行的函数
IntFuture f = async(get_new_user_id);
//future不允许复制,f是future类型
futures.push_back(std::move(f));
}
set <int> ids;
//"auto&"使用引用,因为future不允许复制
for(auto& f : futures)
{
int id = f.get();
if(ids.find(id) != ids.end())
{
cout << "该ID已经存在了:" << id << endl;
system("pause");
continue;
}
ids.insert(id);
}
}
main()函数不变,还是调用test(),重新编译后多次运行,应该可以看到重复的ID。如果着急看到效果,则可以在main()中写个循环,连续调用test()20次,并发冲突问题发生在这一行代码
return ++_id;
汇编中,"++_id"至少被拆成三个步骤:
先将内存中现有“_id”的值复制到CPU寄存器,然后针对寄存器进行自增1操作,最后将自增得到的结果从寄存器复制到源内存。
如果有两个线程同时执行(哪怕是交叉执行以上三个步骤),重复号码就产生了。
解决办法自然是:不要让两个(或更多)线程同时执行“++ id”这个操作,换句话说,就是最多只让一个线程在执行“++id”这个操作。