Bootstrap

Linux/C++多线程编程学习笔记——线程同步、锁

目录

1.为什么要线程同步

2.线程同步的方式

2.1互斥锁

2.2 读写锁

2.3 条件变量

2.4 信号量

1.为什么要线程同步

多个线程同时对内存中的共享资源进行访问时,当一个线程对共享资源进行修改时,其他线程得到的依然是修改前的内容,这样就存在巨大的隐患

比如三个ABC人共用一张银行卡,这三个人就是三个线程银行卡就是共享资源,假如银行卡里有100块钱,这三个人同时取这100块钱,如果不做线程同步,那么三个人都能同时从卡里取出100块钱,也就是总共取出300块钱,那银行岂不是血亏

所以线程同步就是多个线程同时对共享内存提出访问申请时,需要按照先后次序进行访问(线性访问),就是说当A正在取钱时,BC就要等待A取完才能取

未进行线程同步的代码示例(刷银行卡的例子):

int card;

void* aGetMoney(void* arg)
{
    ostringstream* str = (ostringstream*)arg;
    (*str) << "a check: " << card;
    card = 0;

    return NULL;
}
void* bGetMoney(void* arg)
{
    ostringstream* str = (ostringstream*)arg;
    (*str) << "b check: " << card;
    card = 0;

    return NULL;
}
void* cGetMoney(void* arg)
{
    ostringstream* str = (ostringstream*)arg;
    (*str) << "c check: " << card;
    card = 0;

    return NULL;
}

int main(int argc, char** argv)
{
    card = 100;

    ostringstream stra, strb, strc;
    pthread_t tida, tidb, tidc;
    pthread_create(&tida, NULL, aGetMoney, &stra);
    pthread_create(&tidb, NULL, bGetMoney, &strb);
    pthread_create(&tidc, NULL, cGetMoney, &strc);

    pthread_join(tida, NULL);
    pthread_join(tidb, NULL);
    pthread_join(tidc, NULL);

    cout << stra.str() << endl;
    cout << strb.str() << endl;
    cout << strc.str() << endl;

    return 0;
}

结果:

a check: 100
b check: 100
c check: 100

可以看到,未进行线程同步时,即使卡里只有100块钱,ABC可以同时取到100块钱,这是银行不希望看到的

2.线程同步的方式

线程同步的方式有四种:互斥锁、读写锁、条件变量、信号量

共享资源也称为临界资源(一般为全局区或堆区资源),临界资源相关的上下文代码成为临界区

线程同步的本质就是临界区最多同时只能有一个线程访问

对于上述例子,银行卡就是临界资源,对银行卡的存取操作就是临界区,也就是说某一个时间最多只能有一个人对银行卡进行操作

2.1互斥锁

互斥锁就是在临界区的开头加锁,末尾解锁,一个线程加锁操作后,其他线程不能再加锁将被阻塞,直到这个锁被解开,其他线程解除阻塞后再去竞争资源。类比排队上厕所

一般一个临界资源对应一把互斥锁,哪个线程加的锁,哪个线程解锁,就比如上厕所,你进去把门锁上了,别人也不能从外面解锁打开门,更不能再锁一次门

锁的个数一般看共享资源的个数

(1)创建互斥锁:

pthread_mutex_t mutex;

(2)初始化互斥锁:

int pthread_mutex_init(pthread_mutex_t* restrict mutex, 
            const pthread_mutexattr_t* restrict attr);

参数:

* restrict mutex:互斥锁的地址

* restrict attr:属性,默认为NULL

(3)释放互斥锁资源

int pthread_mutex_destroy(pthread_mutex_t *mutex);

(4)加锁

int pthread_mutex_lock(pthread_mutex_t* mutex);

一个线程执行到这里时会判断mutex是不是锁定状态,如果已被锁了则线程阻塞在这里,如果没被锁则线程可以加锁

(5)解锁

int pthread_mutex_unlock(pthread_mutex_t* mutex);

举例:还是上面银行卡的例子,对临界区加锁之后,可以看到,当卡里的钱被a取完后,bc再查卡里就没钱了

int card;

pthread_mutex_t mutex;

void* aGetMoney(void* arg)
{
    ostringstream* str = (ostringstream*)arg;
    pthread_mutex_lock(&mutex);
    (*str) << "a check: " << card;
    card = 0;
    pthread_mutex_unlock(&mutex);

    return NULL;
}
void* bGetMoney(void* arg)
{
    ostringstream* str = (ostringstream*)arg;
    pthread_mutex_lock(&mutex);
    (*str) << "b check: " << ca

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;