C++11多线程学习(1)atomic_flag

d学了这么久我才刚刚知道C++11居然对多线程有如此丰富的支持,一方面感慨自己学的还是太少了,一方面真的很开心可以告别自己封装mutex,线程,currentthread这些东西时代了。最重要的一点,在windows上写的代码和在linux上也可以编译运行,这意味着可以使用宇宙第一IDE来调试多线程程序。我会用这个系列记录自己学习C++11多线程的过程。

C++11中引入了thread这个头文件,终于把线程当成对象来操作了,以前还要自己封装,还要自己定义每个线程的私有空间,现在C++11把一切都帮我们做了。

除了thread还有atomic,可以让我们让我们非常轻松的申请一个原子数,其中还包含一个atomic_flag,我们可以用它来制作一个自旋锁。

首先来看看atomic_flag,它内含一个标志位,它支持两个操作test_and_set()和clear(),在使用之前用宏ATOMIC_FLAG_INIT初始化,初始化意义是吧其中的标志位置位0,test_and_set检测其中的标志位,如果是0就置位1,返回0,如果是1就不变,返回1,这些操作都是原子性的,clear用于把标志位置位0。

下面的程序可以用来熟悉上述的几个操作。大致的意思是创建10个线程,在确保所有线程都创建好了之后,开始计数,第一个计数完毕的输出自己的id。其中的全局原子数ready起到了类似发令枪的作用,yield用于防止忙等待,如果此时ready还没有准备好,就把时间片交出去。

#include<iostream>
#include<atomic>
#include<thread>
#include<vector>
using namespace std;
atomic<bool> ready(false);
atomic_flag winner=ATOMIC_FLAG_INIT;
void count1m(int id)
{
	while(!ready)
		this_thread::yield();
	for(int i=0;i<1000000;i++);
	if(!winner.test_and_set())
		cout<<id<<endl;
}
int main()
{
	vector<thread>threads;
	for(int i=0;i<10;i++)
		threads.push_back(thread(count1m,i));
	ready=true;
	for(int i=0;i<10;i++)
		threads[i].join();
}
#include<iostream>
#include<atomic>
#include<thread>
#include<vector>
using namespace std;
atomic_flag flag=ATOMIC_FLAG_INIT;
void f(int id)
{
	while(flag.test_and_set())
            ;
	cout<<id<<endl;
	flag.clear();
}
int main ()
{
	vector<thread>threads;
	for(int i=0;i<10;i++)
		threads.push_back(thread(f,i));
	for(int i=0;i<threads.size();i++)
		threads[i].join();

}

上面这段代码便已经是一个简单的自选锁了,while构成加锁动作,clear构成解锁动作,如果此时锁已经被获取,就在while处忙等待,直到获取锁为止。

class Mylock
{
private:
	atomic_flag _lock;
public:
	Mylock()_lock(ATOMIC_FLAG_INIT)
	{ }
	void lock()
	{
		while(_lock.test_and_set())
			;
	}
	void unlock()
	{
		_lock.clear();
	}
};
Mylock splock;
void f(int id)
{
	splock.lock();
	cout<<id<<endl;
	splock.unlock();
}
int main ()
{
	vector<thread>threads;
	for(int i=0;i<10;i++)
		threads.push_back(thread(f,i));
	for(int i=0;i<threads.size();i++)
		threads[i].join();

}

但我发现网上大多数的写法都是这样的

class Mylock
{
private:
	atomic_flag _lock;
public:
	Mylock()_lock(ATOMIC_FLAG_INIT)
	{ }
	void lock()
	{
		while(_lock.test_and_set(memory_order_require))
			;
	}
	void unlock()
	{
		_lock.clear(memory_order_release);
	}
};

其中涉及到test_and_set和clear的参数

他们的参数是内存顺序,是一个枚举类型

typedef enum memory_order
    {
      memory_order_relaxed,   //不对执行顺序做任何保证
      memory_order_consume,    //本线程中所有有关本原子类型的操作,必须等到本条原子操作完成之后进行
      memory_order_acquire,    //本线程中,后续的读操作必须在本条原子操作完成后进行
      memory_order_release,    // 本线程中,之前的写操作完成后才执行本条原子操作
      memory_order_acq_rel,    //memory_order_acquire和memory_order_release 效果的合并
      memory_order_seq_cst     //顺序一致
} memory_order;

大致的目的应该是防止编译器对语句执行顺序做优化。

猜你喜欢

转载自blog.csdn.net/qq_33113661/article/details/88909404