文章目录
C++11中提供了thread线程库,它本质上和pthread库差不多,只不过被封装了,同时它还是可以跨平台的
thread
构造函数
1 2 3
| thread() noexcept; template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args);
|
注意
- 无参构造就只是开一个线程,但是不会工作,不会执行
- thread的第一个元素是可调用对象
- 后面的是可变参数列表,可以传入任意的参数
- 第一个参数是一个模板参数,所以它是万能引用,既可以传左值也可以传右值
函数指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void print(int x,int y) { cout<<x<<y<<endl; }
void threads() {
thread t1; thread t2(print, 10,2); t2.join(); }
|
Lambda表达式
1 2 3 4 5
| int main() { thread t([](){cout<<"hello world"<<endl;}); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| int main() { int x = 0; mutex mtx; int N = 10000; atomic<int> costtime1(0); thread t1([&] { int begin1 = clock(); mtx.lock(); for (int i = 0; i < N; i++) { x++; } mtx.unlock(); cout<<x<<endl; int end1 = clock(); costtime1 += (end1 - begin1); cout<<costtime1<<endl;}); cout << x << ":" << costtime1 << endl;
int costtime2 = 0; thread t2([&] { int begin2 = clock(); mtx.lock();
for (int i = 0; i < N; i++) { x++; } mtx.unlock(); int end2 = clock(); costtime2 = end2 - begin2; }); t1.join(); t2.join(); cout << x << ":" << costtime1 << endl; return 0; }
|
仿函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Func {
void operator() { cout << std::this_thread::get_id() << "++x " << x << endl; } int main() { Func fc; thread ss(fc); thread s((Func())); }
|
使用类对象成员函数
- 静态成员函数可以
直接使用对应的静态函数
- 使用普通成员函数,除了
有成员函数
,还要有类对象
,类对象可以是一个临时对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class test { public: test() { } ~test() { } static void do_work1() { cout << "do work 1" << endl; std::this_thread::sleep_for(std::chrono::seconds(2)); } void do_work2() { cout << "do work 2" << endl; std::this_thread::sleep_for(std::chrono::seconds(2)); } void do_work3(string& arg, int x, int y) { cout << "do work 3 x=" << x << "y=" << y << endl; std::this_thread::sleep_for(std::chrono::seconds(2)); } };
void test_thread(int &data) { cout << "thread data=" << data << endl; std::this_thread::sleep_for(std::chrono::seconds(2)); } int main() { int data=10; thread t1(&test_thread,ref(data)); thread t2(&test::do_work1); test t; thread t3(&test::do_work2,t); thread t3(&test::do_work3,t,std::ref("test"),10,20);
return 0; }
|
拷贝构造
thread线程库不允许进行拷贝构造,所以直接把它给删除掉
1
| thread (const thread&) = delete;
|
赋值重载
不允许赋值一个左值对象,类比拷贝构造
但是可以用一个右值对象来赋值
1 2 3
| thread& operator= (thread&& rhs) noexcept; copy [deleted] (2) thread& operator= (const thread&) = delete;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void test() { int n; cin >> n; vector<thread> vthds; vthds.resize(n);
for (auto& e : vthds) { e = thread(print,100,2); } }
|
获取id
1
| std::this_thread::get_id()
|
1 2 3 4 5 6 7 8 9 10 11 12
| class Func {
void operator() { cout << std::this_thread::get_id() << "++x " << x << endl; } int main() { thread s((Func())); } };
|
sleep
1
| std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
join和detach
- join :就是在一个线程还没处理完之前,主线程都要一直等着这个线程做,直到新线程处理完了,才会放开主线程,
- detach: 就是会把主线程和新线程分离开来,新线程的事情不影响主线程做事,后台自动回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void print(int x,int y) { cout<<x<<y<<endl; }
int main() { thread s(print,10,20); s.detach(); if(s.joinable())
s.join();
cout<<"hello"<<endl;
return 0; }
|
引用与传参
假如说我们在main函数里面定义了对象想要传到thread里面
- 可以使用指针进行传参
- 不能用左值来进行接收,但是可以使用std::ref( ),之后就能用左值接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void func(int* x) { *x+=10; } void func(int &x) { x += 10; }
int main() { int n = 10; thread t1(func,&n); thread t2(func, std::ref(n)); t1.join(); t2.join(); cout << n << endl; }
|
atomic
为了解决内置类型传参过去的线程安全的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| atomic<int> x(0); atomic_long m{0}; atomic<long> n(2);
void func() { x++; } int main() { thread s(func); thread p(func); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| void threadpool() { atomic<int> x(0); int n, m; cin >> n >> m; vector<thread> vthds; vthds.resize(n); atomic<int> costtime(0); for (size_t i = 0; i < vthds.size(); i++) { vthds[i] = thread([m, &x, &costtime]() { int begin=clock(); for(int i=0;i<m;i++) { x++; } int end=clock(); costtime+=(end-begin); }); } for (auto &e : vthds) { if (e.joinable()) e.join(); } cout << x << endl; cout << costtime << endl; }
|
mutex
线程安全里面的锁资源
- lock就把临界区锁住了
- unlock可以把解锁
- try_lock:如果这个锁已近被别人用了,就啥也不干直接返回,如果这个锁是空闲的,就把对应的线程给锁住
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| int x = 0; mutex mtx; void Func(int& n) { mtx.lock(); for (int i = 0; i < n; i++) {
cout << std::this_thread::get_id() << "++x " << x << endl;
++x; } mtx.unlock(); }
int main() { int n=10; thread th(Func,std::ref(n)); return 0; }
|
lock_guard
我们使用锁会出现一种情况,一把锁锁住之后,但是里面就调用throw,抛异常之后,就会到catch里面,就把后续代码都跳过了,这个就会造成死锁的问题
所以我们就可以用一个RAII机制的锁,在调用的时候构造,上锁,在析构的时候解锁
lock_guard 只能在作用域结束后才能解锁
模拟实现lock_guard
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <class Lock> class LockGuard { private: Lock& _lock;
public: LockGuard(Lock& lock) :_lock(lock) { _lock.lock(); } ~LockGuard() { _lock.unlock(); } };
|
unique_lock
和lock_guard类似,但是可以支持在作用域结束之前解锁
所以更加推荐使用unique_lock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| void vfunc(vector<int> &vt, int x, int base, mutex &mtx) { try { if (base == 200) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
for (int i = 0; i < x; i++) {
unique_lock<mutex> lockk(mtx);
vt.push_back(i);
if (base == 100 && i == 3) throw bad_alloc();
} } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } }
void test() { thread t1, t2; vector<int> vt;
mutex mtx; t1 = thread(vfunc, std::ref(vt), 5, 100, std::ref(mtx)); t2 = thread(vfunc, std::ref(vt), 10, 200, std::ref(mtx));
t1.join(); t2.join();
for (auto e : vt) { cout << e << " "; } }
|
cond_variable
wait
1 2
| template <class Predicate> void wait (unique_lock<mutex>& lck, Predicate pred);
|
wait后面的参数是可调用对象,同理,也是函数指针,lambda表达式,仿函数,当返回为true时,才会唤醒,否则一直阻塞着
notify_one:唤醒一个线程
实战
交替打印奇偶数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <iostream> #include <thread> #include <mutex> #include <condition_variable> using namespace std;
void test1() { int n = 100; int i = 0; mutex mtx; bool flag = false; condition_variable cv; thread t1([&]() { while(i<n) { unique_lock<mutex> lock(mtx);
cv.wait(lock,[&](){return i%2==1;}); cout<<this_thread::get_id()<<"->"<<i<<" "<<endl; i++; flag=!flag; cv.notify_one();
} }); thread t2([&]() { while(i<n){ unique_lock<mutex> lock(mtx); cv.wait(lock,[&](){return i%2==0;}); cout<<this_thread::get_id()<<"->"<<i<<" "<<endl;; i++; flag=!flag; cv.notify_one(); } }); t1.join(); t2.join(); }
int main() { test1(); return 0; }
|