c++11多线程编程
添加时间:2013-6-16 点击量:
标准线程库,c++11引入,包含原子操纵库、互斥锁、前提变量。。。
一、线程库<thread>
创建线程的四种办法:
1. 经由过程全局函数创建线程
线程类的机关函数是变参机关函数,第一个参数是线程函数,后面的参数为线程函数的参数(参数经由过程值传递体式格式,若要引用传递须加std::ref())。
thread t1 (counter, 1, 6); //void counter(int, int);
2. 经由过程函数对象创建线程
//class Counter 实现 operator()
1) thread t1{Counter(1, 20)}; //c++同一推荐办法
2) Counter c(1, 20);
thread t2(c);
3) thread t3(Counter(1,20));
斗劲第一种和第三种机关体式格式,若是函数对象的机关函数不须要任何参数。 thread t3(Counter());是不可的,因为编译器会认为你在声明一个函数,函数名为t3,此时只能用第一种机关体式格式。
3. 经由过程lambda表达式创建线程
今朝还没进修lambda表达式,略过......
4. 经由过程成员函数创建线程
// class Counter c();
thread t{&Counter::process, &c};
一般常见的是一个类本身创建一个后台处理惩罚线程:thread t{&Counter::process, this};
线程本地存储 thread_local
thread_local int n;
n作为线程参数传递给线程,那么每个线程有一个n的副本,在线程全部生命周期中存在,且只初始化一次,如同static局部变量。
二、原子操纵库<atomic>
多线程编程经常须要操纵共享的内存,在读/写过程中会导致竞争前提。
例如:
int counter = 0;
............
++counter; //因为++counter不时原子操纵,多个线程中呈现此操纵时不是线程安然的。
应当用:
atomic<int> counter(0); //等效于 atomic_int counter(0);
............
++counter; //此时多个线程履行++counter是一个原子操纵,是线程安然的。
例:
void func( std::atomic<int>& counter)
{
for( int i=0; i<1000; ++i )
++counter;
}
int main()
{
std::atomic<int> counter(0);
std::vector<std::thread> threads;
for( int i=0; i<10; ++i )
//线程参数老是值传递,若要传递引用,须加std::ref()。(头文件<functional>中)
threads.push_back( std::thread{ func, std::ref(counter)} );
for( auto& t : threads )
t.join(); //调用join,若是线程未停止,则main函数梗阻于此。
std::count<<Result=<<counter<<std::endl;
return 0;
}
/join的调用会导致调用线程梗阻于此,若不调用线程梗阻,但又想知道被调线程是否停止,该当用其它体式格式,例如消息.../
三、互斥 <mutex>
编写多线程必须额外留心操纵次序,若是无法避免线程共享数据,则必须供给同步机制,包管一次只有一个线程能更改数据。应用互斥解决竞争前提,可能导致死锁。
1. 互斥体类
1) 非按时互斥体类 std::mutex std::recursive_mutex
lock() : 测验测验获取锁,并且梗阻直到获取锁。
try_lock() : 测验测验获取锁,并立即返回,成功获取返回true,不然false。
unlock() : 开释锁。
mutex与recursive_mutex的差别在于,前者已经获得所后不得再测验测验获取,这会死锁,后者能递归获取,重视开释次数应与获取次数相等。
2) 按时互斥锁类 std::timed_mutex std::recursive_timed_mutex
lock() , try_lock() , unlock()
try_lock_for(rel_time) : 指定相对时候内获得返回true, 超时返回false。
try_lock_until(abs_time) : 指定体系绝对时候内获得返回true, 超时返回false。
timed_mutex与recursive_timed_mutex差别同上。
2. 锁类
锁类是一个包装器,析构函数会主动开释接洽关系的互斥体。
1) 简单锁 std::lock_guard
其机关函数会请求获得互斥体,并梗阻直到获得锁。
2) 错杂锁 std::unique_lock
explict unique_lock( mutex_type& m); //梗阻直到获得锁。
unique_lock(mutex_type& m, defer_lock_t) noexcept; //保存一个互斥体引用,不会立即测验测验获得锁。锁可以在今后获得。
unique_lock(mutex_type& m, try_to_lock_t); //测验测验获得引用的互斥锁,未能获得也不梗阻。
unique_lock(mutex_type& m, adopt_lock_t); //该锁假定线程获得引用的互斥锁,并负责经管这个锁。
template<class Clock, class Duration>
unique_lock(mutex& m, const chrono::time_point<Clock, Duration>& abs_time); //测验测验获取该锁,直到跨越给定的绝对时候。
template<class Rep, class Period>
unique_lock(mutex& m, const chrono::duration<Rep, Period>& rel_time); //测验测验获取该锁,直到跨越给定的相对时候。
unique_lock类还支撑lock(), try_lock(), try_lock_for(), try_lock_until()等办法。
经由过程owns_lock()查看是否获得了这个锁;也可以用if对unique_lock对象直接断定是否获得锁,因为它定义了bool()运算符。
3. 获得多个互斥体对象上的锁
1) 泛型lock可变参数模板函数
template <class L1, class L2, class...L3>
void lock(L1&, L2&, L3&...);
按次序锁定,若是一个互斥体抛出异常,会对已获得的锁unlock。
2) 泛型try_lock
template <class L1, class L2, class...L3>
int try_lock(L1&, L2&, L3&...);
经由过程次序调用互斥体对象的try_lock,成功返回-1,失败返回从0开端的地位索引,并对已获得的锁unlock。
参数次序每次应对峙一致, 不然易死锁。
4. std::call_once std::once_flag
包管call_once调剂的函数只被履行一次。
5. 实例:
// 1. 简单锁
mutex mMutex;
lock_guard<mutex> mLock(mMutex);
// 2. 按时锁
timed_mutex mTimeMutex;
unique_lock<timed_mutex> mLock(mTimedMutex, chrono::milliseconds(200));
// 3. 泛型
mutex mut1;
mutex mut2;
unique_lock<mutex> lock1(mut1, defer_lock_t());
unique_lock<mutex> lock2(mut2, defer_lock_t());
lock(lock1, lock2);
// 4. 双重搜检锁定算法 (庖代call_once的用法)
class MyClass
{
public:
void init() { p = new int(0); cout<<Init<<endl;}
private:
int p;
}
MyClass var;
bool initialized = false;
mutex mut;
void func()
{
if( ! initialized) //一次搜检
{
unique_lock<mutex> lock1(mut);
if( ! initialized) //两次搜检
{
var.init();
initialized = true;
}
}
cout<<OK<<endl;
}
//两次搜检initialized。获得锁之前和获得锁之后,确保init只调用一次。
四、前提变量 <condition_variable>
1. std::condition_variable 只能守候unique_lock<mutex>的前提变量
notify_one(); //唤醒守候这个前提变量的线程之一
notify_all(); //唤醒所有守候这个前提变量的线程
// 1)前提是已经获得lk的锁
// 2)调用wait会unlock lk,然后守候
// 3)当被唤醒后 lock lk
wait( unique_lock<mutex>& lk);
wait_for(unique_lock<mutex>& lk, const chrono::duration<Rep,Period>& rel_time);
wait_until(unique_lock<mutex>&lk, const chrono::time_point<Clock,Duration>& abs_time);
2. std::condition_variable_any 支撑任何类型的Lock类
3.
//例:向队列中参加数据,当队列不为空时,后台线程被唤醒处理惩罚数据
std::queue<std::string> mQueue;
std::mutex mMutex;
std::condition_variable mCondVar;
//向队列参加数据的线程
unique_lock<mutex> lock(mMutex);
mQueue.push( data);
mCondVar.notify_all();
//后台处理惩罚数据的线程
unique_lock<mutex> lock(mMutex);
while(true)
{
//1.先开释lock 2.然后守候被唤醒 3.被唤醒后守候获取lock
mCondVar.wait(lock);
// process...
}
五、 future
promise/future模型便利获取线程返回的成果、线程间通信、处理惩罚异常
文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》
标准线程库,c++11引入,包含原子操纵库、互斥锁、前提变量。。。
一、线程库<thread>
创建线程的四种办法:
1. 经由过程全局函数创建线程
线程类的机关函数是变参机关函数,第一个参数是线程函数,后面的参数为线程函数的参数(参数经由过程值传递体式格式,若要引用传递须加std::ref())。
thread t1 (counter, 1, 6); //void counter(int, int);
2. 经由过程函数对象创建线程
//class Counter 实现 operator()
1) thread t1{Counter(1, 20)}; //c++同一推荐办法
2) Counter c(1, 20);
thread t2(c);
3) thread t3(Counter(1,20));
斗劲第一种和第三种机关体式格式,若是函数对象的机关函数不须要任何参数。 thread t3(Counter());是不可的,因为编译器会认为你在声明一个函数,函数名为t3,此时只能用第一种机关体式格式。
3. 经由过程lambda表达式创建线程
今朝还没进修lambda表达式,略过......
4. 经由过程成员函数创建线程
// class Counter c();
thread t{&Counter::process, &c};
一般常见的是一个类本身创建一个后台处理惩罚线程:thread t{&Counter::process, this};
线程本地存储 thread_local
thread_local int n;
n作为线程参数传递给线程,那么每个线程有一个n的副本,在线程全部生命周期中存在,且只初始化一次,如同static局部变量。
二、原子操纵库<atomic>
多线程编程经常须要操纵共享的内存,在读/写过程中会导致竞争前提。
例如:
int counter = 0;
............
++counter; //因为++counter不时原子操纵,多个线程中呈现此操纵时不是线程安然的。
应当用:
atomic<int> counter(0); //等效于 atomic_int counter(0);
............
++counter; //此时多个线程履行++counter是一个原子操纵,是线程安然的。
例:
void func( std::atomic<int>& counter)
{
for( int i=0; i<1000; ++i )
++counter;
}
int main()
{
std::atomic<int> counter(0);
std::vector<std::thread> threads;
for( int i=0; i<10; ++i )
//线程参数老是值传递,若要传递引用,须加std::ref()。(头文件<functional>中)
threads.push_back( std::thread{ func, std::ref(counter)} );
for( auto& t : threads )
t.join(); //调用join,若是线程未停止,则main函数梗阻于此。
std::count<<Result=<<counter<<std::endl;
return 0;
}
/join的调用会导致调用线程梗阻于此,若不调用线程梗阻,但又想知道被调线程是否停止,该当用其它体式格式,例如消息.../
三、互斥 <mutex>
编写多线程必须额外留心操纵次序,若是无法避免线程共享数据,则必须供给同步机制,包管一次只有一个线程能更改数据。应用互斥解决竞争前提,可能导致死锁。
1. 互斥体类
1) 非按时互斥体类 std::mutex std::recursive_mutex
lock() : 测验测验获取锁,并且梗阻直到获取锁。
try_lock() : 测验测验获取锁,并立即返回,成功获取返回true,不然false。
unlock() : 开释锁。
mutex与recursive_mutex的差别在于,前者已经获得所后不得再测验测验获取,这会死锁,后者能递归获取,重视开释次数应与获取次数相等。
2) 按时互斥锁类 std::timed_mutex std::recursive_timed_mutex
lock() , try_lock() , unlock()
try_lock_for(rel_time) : 指定相对时候内获得返回true, 超时返回false。
try_lock_until(abs_time) : 指定体系绝对时候内获得返回true, 超时返回false。
timed_mutex与recursive_timed_mutex差别同上。
2. 锁类
锁类是一个包装器,析构函数会主动开释接洽关系的互斥体。
1) 简单锁 std::lock_guard
其机关函数会请求获得互斥体,并梗阻直到获得锁。
2) 错杂锁 std::unique_lock
explict unique_lock( mutex_type& m); //梗阻直到获得锁。
unique_lock(mutex_type& m, defer_lock_t) noexcept; //保存一个互斥体引用,不会立即测验测验获得锁。锁可以在今后获得。
unique_lock(mutex_type& m, try_to_lock_t); //测验测验获得引用的互斥锁,未能获得也不梗阻。
unique_lock(mutex_type& m, adopt_lock_t); //该锁假定线程获得引用的互斥锁,并负责经管这个锁。
template<class Clock, class Duration>
unique_lock(mutex& m, const chrono::time_point<Clock, Duration>& abs_time); //测验测验获取该锁,直到跨越给定的绝对时候。
template<class Rep, class Period>
unique_lock(mutex& m, const chrono::duration<Rep, Period>& rel_time); //测验测验获取该锁,直到跨越给定的相对时候。
unique_lock类还支撑lock(), try_lock(), try_lock_for(), try_lock_until()等办法。
经由过程owns_lock()查看是否获得了这个锁;也可以用if对unique_lock对象直接断定是否获得锁,因为它定义了bool()运算符。
3. 获得多个互斥体对象上的锁
1) 泛型lock可变参数模板函数
template <class L1, class L2, class...L3>
void lock(L1&, L2&, L3&...);
按次序锁定,若是一个互斥体抛出异常,会对已获得的锁unlock。
2) 泛型try_lock
template <class L1, class L2, class...L3>
int try_lock(L1&, L2&, L3&...);
经由过程次序调用互斥体对象的try_lock,成功返回-1,失败返回从0开端的地位索引,并对已获得的锁unlock。
参数次序每次应对峙一致, 不然易死锁。
4. std::call_once std::once_flag
包管call_once调剂的函数只被履行一次。
5. 实例:
// 1. 简单锁
mutex mMutex;
lock_guard<mutex> mLock(mMutex);
// 2. 按时锁
timed_mutex mTimeMutex;
unique_lock<timed_mutex> mLock(mTimedMutex, chrono::milliseconds(200));
// 3. 泛型
mutex mut1;
mutex mut2;
unique_lock<mutex> lock1(mut1, defer_lock_t());
unique_lock<mutex> lock2(mut2, defer_lock_t());
lock(lock1, lock2);
// 4. 双重搜检锁定算法 (庖代call_once的用法)
class MyClass
{
public:
void init() { p = new int(0); cout<<Init<<endl;}
private:
int p;
}
MyClass var;
bool initialized = false;
mutex mut;
void func()
{
if( ! initialized) //一次搜检
{
unique_lock<mutex> lock1(mut);
if( ! initialized) //两次搜检
{
var.init();
initialized = true;
}
}
cout<<OK<<endl;
}
//两次搜检initialized。获得锁之前和获得锁之后,确保init只调用一次。
四、前提变量 <condition_variable>
1. std::condition_variable 只能守候unique_lock<mutex>的前提变量
notify_one(); //唤醒守候这个前提变量的线程之一
notify_all(); //唤醒所有守候这个前提变量的线程
// 1)前提是已经获得lk的锁
// 2)调用wait会unlock lk,然后守候
// 3)当被唤醒后 lock lk
wait( unique_lock<mutex>& lk);
wait_for(unique_lock<mutex>& lk, const chrono::duration<Rep,Period>& rel_time);
wait_until(unique_lock<mutex>&lk, const chrono::time_point<Clock,Duration>& abs_time);
2. std::condition_variable_any 支撑任何类型的Lock类
3.
//例:向队列中参加数据,当队列不为空时,后台线程被唤醒处理惩罚数据
std::queue<std::string> mQueue;
std::mutex mMutex;
std::condition_variable mCondVar;
//向队列参加数据的线程
unique_lock<mutex> lock(mMutex);
mQueue.push( data);
mCondVar.notify_all();
//后台处理惩罚数据的线程
unique_lock<mutex> lock(mMutex);
while(true)
{
//1.先开释lock 2.然后守候被唤醒 3.被唤醒后守候获取lock
mCondVar.wait(lock);
// process...
}
五、 future
promise/future模型便利获取线程返回的成果、线程间通信、处理惩罚异常
文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》