C++多核多线程同步实现
使用Makefile C++11工程模拟dsp的多核同步逻辑,使用多线程模拟多核,多个线程通过C++11的条件变量实现同步。
当某一线程执行到同步函数syn_func时,判断是否其他线程执行到此处了,若有其他线程没有执行到此处,本线程就应阻塞。
当最后一个线程执行到同步函数时,通知所有线程解除阻塞,实现同步。
为了实现这个过程,首先需要一个条件变量,阻塞各线程。其次,需要一个全局变量,作为各线程运行到位的依据。所以一个简单的实现:
1 mutex dsp_lock;
2 condition_variable dsp_cva; //条件变量
3 vu32 dsp_cva_no=0; //多核同步的数量
4
5 void syn_func() //多核同步
6 {
7 unique_lock<mutex> lk(dsp_lock);
8 dsp_cva_no++;
9 if(dsp_cva_no>=NCORES) //若自己是最后一个
10 {
11 dsp_cva_no=0;
12 dsp_cva.notify_all();
13 }
14 else dsp_cva.wait(lk); //若不是,就等,单独的wait可能造成虚假唤醒
15 }
这个实现逻辑非常简单,但条件变量的wait函数可能造成虚假唤醒,导致失步。所以必须使用带条件的wait函数来实现。
条件变量的wait函数在带锁的条件下执行条件函数,确定是否需要等待,若需要等待,就解除互斥锁,并阻塞线程。
由于解除阻塞时多了一个 加锁-判断-解锁 的过程,先完成的线程会立刻执行同步点之后的程序,可能会修改同步变量,从而导致后执行的线程判断错误。
所以需要修改同步变量的机制,改为记录每个核的同步序号,每次同步时序号加1。为处理序号溢出问题,判断时,判断本线程序号是否等于其他线程的序号减1,从而得出其他线程是否已经进入下一个同步点的结论。
1 mutex dsp_lock;
2 condition_variable dsp_cva; //条件变量
3 vu32 dsp_cva_no[NCORES]={0}; //每一个核1个序号
4
5 void syn_func() //多核同步
6 {
7 unique_lock<mutex> lk(dsp_lock);
8 int cn=coreNum;
9 dsp_cva_no[cn]++;
10 dsp_cva.wait(lk,[cn]()
11 {
12 for(int i=0;i<NCORES;i++)
13 {
14 if(dsp_cva_no[cn]!=dsp_cva_no[i] && //若有不同步的
15 dsp_cva_no[cn]!=(dsp_cva_no[i]-1)) //或其它核已经进入下个了
16 {
17 return 0; //未同步,有的核没跟上
18 }
19 }
20 return 1;
21 });
22 dsp_cva.notify_all();
23 }
程序中,调用同步函数直接就调用wait函数,通过条件函数判断是否需要阻塞。阻塞的标准是各线程步骤序号不同。最后一个同步的线程不经阻塞,直接调用notify_all,通知所有线程开始执行。即使第一个线程已经执行到下一个同步点,修改了同步变量,其他线程判断同步变量时,也能判断出自己是落后的,从而解除阻塞。
所有线程解除阻塞时,都会调用notify_all函数,但不会对同步造成影响,因为线程阻塞时会判断同步变量才解除阻塞。