[TOC]
关于多线程
多线程可以同时做多件事 ,且可以互相交流影响 。
首先包含头文件thread ,例:
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 #include <iostream> #include <thread> static bool s_Finished = false ;void Dowork () { using namespace std::chrono_literals; std::cout<<"started thread id = " <<std::this_thread::get_id ()<<std::endl; while (!s_Finished) { std::cout<< "working...\n" ; std::this_thread::sleep_for (1 s); } } int main () { std::thread worker (Dowork) ; std::cin.get (); s_Finished = true ; worker.join (); std::cout<<"Finished." <<std::endl; std::cout<<"started thread id = " <<std::this_thread::\ get_id ()<<std::endl; std::cin.get (); return 0 ; }
若编译报链接错误——"对‘pthread_create’未定义的引用" ,则在g++编译时 添加参数-lpthread或-pthread ,或者在CMakeLists.txt 中添加target_link_libraries(xout
-lpthread)
关于互斥锁mutex
为了实现线程之间的交流,需要用到互斥锁。做到在多个线程同时对一个内存空间操作的时候,保证线程安全。
如果不加锁 效果如下:
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 #include <iostream> #include <thread> #include <mutex> using namespace std;class mutexTest {public : char _character; mutexTest (char character):_character(character) { } void run () { for (int i = 0 ; i < 10 ; i++) { cout<<_character; } } }; int main () { mutexTest testClass1 ('*' ) ; mutexTest testClass2 ('&' ) ; thread* testThread1 = new thread (&mutexTest::run, &testClass1); thread* testThread2 = new thread (&mutexTest::run, &testClass2); testThread1->join (); testThread2->join (); cin.get (); 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 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <thread> #include <mutex> using namespace std;class mutexTest {public : char _character; mutex* _mut; mutexTest (char character):_character(character) { } void run () { _mut->lock (); for (int i = 0 ; i < 10 ; i++) { cout<<_character; } _mut->unlock (); } }; int main () { mutex mut; mutexTest testClass1 ('*' ) ; testClass1._mut = &mut; mutexTest testClass2 ('&' ) ; testClass2._mut = &mut; thread* testThread1 = new thread (&mutexTest::run, &testClass1); thread* testThread2 = new thread (&mutexTest::run, &testClass2); testThread1->join (); testThread2->join (); cin.get (); return 0 ; }
关于unique_lock
unique_lock 相当于作用域智能指针unique_ptr ,unique_ptr利用局部变量生命周期 出作用域即释放内存(利用析构函数 ),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 #include <iostream> #include <thread> #include <mutex> using namespace std;class mutexTest {public : char _character; mutex* _mut; mutexTest (char character):_character(character) { } void run () { unique_lock<mutex> lock (*_mut) ; for (int i = 0 ; i < 10 ; i++) { cout<<_character; } } }; int main () { mutex mut; mutexTest testClass1 ('*' ) ; testClass1._mut = &mut; mutexTest testClass2 ('&' ) ; testClass2._mut = &mut; thread* testThread1 = new thread (&mutexTest::run, &testClass1); thread* testThread2 = new thread (&mutexTest::run, &testClass2); testThread1->join (); testThread2->join (); cin.get (); return 0 ; }
关于并发
并发 有两大需求,一是互斥 ,二是等待 。互斥是因为线程间存在共享数据 ,等待则是因为线程间存在依赖 。
互斥 的话,通过互斥锁能搞定,常见的有依赖操作系统的mutex 。
信号量 、条件变量 ,是为了解决等待 需求。
如考虑实现生产者消费者队列 ,生产者和消费者各是一个线程,线程间共享产品数据队列 。一个明显的依赖 是,消费者线程依赖生产者线程
push
元素进队列,然后才能消费产品。若不使用信号量、条件变量,则只能使用轮询(poll)的方式,循环查看队列是否为空,这种方式太过占用cpu资源。
信号量
使用信号量 解决进程间的依赖 :
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 #include <thread> #include <semaphore> #include <queue> extern std::semaphore sem; extern std::queue que;void produce (Good good) { que.push (good); sem.signal (); } Good consume () { sem.wait (); auto g = que.pop (); return g; } std::semaphore sem; std::queue que; int main () { std::thread Productor (produce) ; std::thread Consumer (consume) ; Productor.join (); Consumer.join (); }
信号量到互斥锁
将信号量初始值设为1 ,解决共享内存 的互斥竞争 问题,即一个时刻只有一个“人”访问这块共享内存,不然就数据混乱了。
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 #include <thread> #include <semaphore> #include <queue> extern std::semaphore sem;extern std::semaphore mutex;extern std::queue que;void produce (Good good) { mutex.wait (); que.push (good); mutex.signal (); sem.signal (); } Good consume () { sem.wait (); mutex.wait (); auto g = que.pop (); mutex.signal (); return g; } std::semaphore sem; std::semaphore mutex; std::queue que; int main () { std::thread Productor (produce) ; std::thread Consumer (consume) ; Productor.join (); Consumer.join (); }
条件变量
条件变量 解决依赖问题。理解条件变量需要注意一点,条件变量自身并不包含条件判断 ,所以它通常和
if(或者while) 一起使用,结合才叫条件变量。
对于生产者消费者模型 来说,如果只有一个消费者 可以用
if ,但如果存在多个消费者 则需要用
while 。因为当 broadcast
广播 条件满足时,多个 处于睡眠状态的消费者都在尝试加锁,其中某个首先加锁成功 ,将唯一的产品消费了 ,再解锁 。这时第二个 尝试加锁的消费者也加锁成功 了,如果不进入循环再判断一次条件,直接去消费产品,会发现无产品可消费 !所以需要用
while
,条件满足加锁后再进 入循环判断 一次条件,满足则跳出循环,不满足则释放锁进入睡眠 。(循环时注意不要整个函数都锁着,不然别人没有空隙去抢锁。要保证锁的粒度尽量小! )。例 :
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <string> #include <iostream> extern std::condition_variable cond;extern std::mutex mut;extern std::queue<std::string> que;void produce () { char good[256 ]; using namespace std::chrono_literals; while (1 ) { std::this_thread::sleep_for (0.1 s); std::lock_guard<std::mutex> lk (mut) ; std::cout<<"\n可以生产产品了!" <<std::endl; std::cin.getline (good, 256 ); que.push (good); std::this_thread::sleep_for (0.4 s); cond.notify_all (); } } void consume () { std::string good; using namespace std::chrono_literals; while (1 ) { std::this_thread::sleep_for (3 s); std::unique_lock<std::mutex> lk (mut) ; while (que.empty ()) cond.wait (lk); std::cout<<"\n开始消费了!" <<std::endl; good = que.front (); que.pop (); std::cout<<"产品是:" << good << std::endl; } } std::condition_variable cond; std::mutex mut; std::queue<std::string> que; int main () { std::thread Productor (produce) ; std::thread Consumer1 (consume) ; std::thread Consumer2 (consume) ; Productor.join (); Consumer1.join (); Consumer2.join (); }
关于计时
包含头文件chrono :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> #include <chrono> #include <thread> int main () { using namespace std::chrono_literals; auto start = std::chrono::high_resolution_clock::now (); std::this_thread::sleep_for (1 s); auto end = std::chrono::high_resolution_clock::now (); std::chrono::duration<float > duration = end - start; std::cout<< duration.count () << "s" <<std::endl; std::cin.get (); 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <chrono> #include <thread> struct Timer { std::chrono::_V2::system_clock::time_point start,end; std::chrono::duration<float > duration; Timer () { start = std::chrono::high_resolution_clock::now (); } ~Timer () { end = std::chrono::high_resolution_clock::now (); duration = end - start; float ms = duration.count () * 1000.0f ; std::cout << "Timer took: " << ms << "ms " <<std::endl; } }; void funtion () { Timer timer; for (int i = 0 ; i<100 ; i++); std::cout<<"Hello" <<std::endl; } int main () { using namespace std::chrono_literals; { Timer timer; std::this_thread::sleep_for (1 s); } funtion (); std::cin.get (); return 0 ; }