线程同步(Linux_C++)

本文深入探讨了线程同步机制,包括互斥锁、读写锁和条件变量的使用方法及原理。介绍了它们的初始化、销毁过程,以及如何通过加解锁进行线程间的同步。并提供了丰富的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程的同步

互斥锁,读写锁,条件变量有两种方式创建

在 posix 互斥锁,读写锁,条件变量有两种方式创建.静态方式和动态方式.

POSIX定义了三个个宏:

    • PTHREAD_MUTEX_INITIALIZER  来静态初始化互斥锁
    • PTHREAD_COND_INITIALIZER  来静态初始化条件变量
    • PTHREAD_RWLOCK_INITIALIZER  来静态初始化读写锁

在 Linux Threads 实现中,pthread_mutex_t,pthread_mutex_t,pthread_cond_t  是一个结构,而 PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_RWLOCK_INITIALIZER 则是一个结构常量。在 pthread_XXX_destroy() 用于注销一个互斥锁,读写锁,条件变量,销毁一个互斥锁,读写锁,条件变量即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁,读写锁,条件变量并不占用任何资源,因此 Linux Threads 中的 pthread_XXX_destroy() 除了检查锁状态以外(锁定状态则返回 EBUSY)没有其他动作

线程和 fork

子进程通过继承整个地址空间的副本,从而父进程哪里继承了所有互斥量,读写锁和条件状态。如果父进程包含多个线程,子进程在 fork 返回以后,如果紧接着不是马上调用 exec 的话,就需要清理锁状态。 在子进程内部只存在一个线程,它是由父进程调用 fork 返回以后,如果父进程中的线程占有锁,子进程同样占有这些锁,问题是子进程同样占有这些锁。问题是子进程不包含这些占有锁的线程副本,所以子进程没有办法知道它占有的那些锁并且需要释放哪些锁。如果子进程从 fork 返回以后马上调用某个 exec 函数,就可以避免这样的问题。这种情况下,老的地址空间被丢弃,所以锁的状态无关紧要。


互斥锁

说明

从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁,对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥量.如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变量为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用.

互斥变量用 pthread_mutex_t 数据类型来表示,在使用互斥变量以前,必须首先对它进行初始化(pthread_mutex_init),可以把它设置为常量 PTHREAD_MUTEX_INITIALIZER (只对静态分配的互斥量),也可以通过调用 pthread_mutex_init 函数进行初始化.如果动态分配互斥量.,那么在释放内存前要调用 pthread_mutex_destroy

对互斥量的加解锁

  • pthread_mutex_lock( .. )
  • pthread_mutex_trylock( .. )
  • pthread_mutex_unlock( .. )

互斥量属性

  • 初始化和销毁互斥量的属性
    • pthread_mutex_init( .. )
    • pthread_mutex_destroy( .. )
  • 获得和设置互斥量的共享属性
    • pthread_mutexattr_getpshared( .. )
    • pthread_mutexattr_setpshared( .. )
  • 设置和修改互斥量的类型属性
    • pthread_mutexattr_gettype( .. )
    • pthread_mutexattr_settype( .. )

互斥变量的进程共享属性和类型属性

  • 共享属性:     相互独立的多个进程把同一个内存区域映射到他们独自的地址空间.就像多个线程访问共享数据一样,多个进程访问共享数据通常也需要同步.如果进程共享互斥量属性设置为 PTHREAD_PROCESS_SHARED ,从多个进程共享的内存区域中分配的互斥量就可以用于这些进程同步.
  • 类型属性:
    • PTHREAD_MUTEX_NORMAL
      • 这种类型的互斥锁不会自动检测死锁。
      • 如果一个线程试图对一个互斥锁重复锁定,将会引起这个线程的死锁。
      • 如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。
      • 如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。
    • PTHREAD_MUTEX_ERRORCHECK
      • 这种类型的互斥锁会自动检测死锁。
      • 如果一个线程试图对一个互斥锁重复锁定,将会返回一个错误代码。
      • 如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。
      • 如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码。
    • PTHREAD_MUTEX_RECURSIVE
      • 如果一个线程对这种类型的互斥锁重复上锁,不会引起死锁。
      • 一个线程对这类互斥锁的多次重复上锁必须由这个线程来重复相同数量的解 ,这样才能解开这个互斥锁,别的线程才能得到这个互斥锁。
      • 如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。
      • 如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码。
      • 这种类型的互斥锁只能是进程私有的(作用域属性为PTHREAD_PROCESS_PRIVATE)
    • PTHREAD_MUTEX_DEFAULT
      • 这种类型的互斥锁不会自动检测死锁。
      • 如果一个线程试图对一个互斥锁重复锁定,将会引起不可预料的结果。
      • 如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。
      • 如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。
      • POSIX标准规定,对于某一具体的实现,可以把这种类型的互斥锁定义为其他类型的互斥锁。

互斥量类型行为

例子

复制代码
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<cstdlib>

using namespace std;

pthread_mutex_t g_mut;

void *th_fn(void *arg)
{
    pthread_mutex_lock(&g_mut);
    cout<<"sub thread"<<endl;
    sleep(1);
    pthread_mutex_unlock(&g_mut);
    return (void *)10;
}

int main()
{
    pthread_t ptid;
    void *tret;

    pthread_mutex_init(&g_mut,NULL);

    pthread_create(&ptid, NULL,th_fn ,NULL);
    
    sleep(1);
    pthread_mutex_lock(&g_mut);
    cout<<"main thread"<<endl;
    sleep(1);
    pthread_mutex_unlock(&g_mut);

    pthread_mutex_destroy(&g_mut);
    pthread_join(ptid, &tret);
    cout<<"thread code 2 exit id = "<<(int)tret<<endl;
    
    return 0;
}
复制代码


读写锁

读写锁的三种状态

  • 读模式下的加锁状态
  • 写模式下的加锁状态
  • 不加锁状态

注意

一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁.当读写锁处于读模式锁住状态时,如果另外的线程视图以写模式加锁,读写锁通常会阻塞随后的读模式请求,这样可以避免读模式长期占用锁,而等待的写锁模式锁一直得不到请求

初始化和销毁读写锁

  • pthread_rwlock_init( .. )
  • pthread_rwlock_destroy( .. )     //如果在调用 pthread_rwlock_destroy 之前就释放了读写锁占用的内存空间,呢么分配给这个锁的资源就丢失了.

对读写锁的加解锁

  • pthread_rwlock_wrlock( .. )
  • pthread_rwlock_trywrlock( .. )
  • pthread_rwlock_rdlock( .. )
  • pthread_rwlock_tryrdlock( .. )
  • ipthread_rwlock_unlock( .. )

获得读写锁属性和释放读写锁属性

  • pthread_rwlockattr_init( .. )
  • pthread_rwlockattr_destroy( .. )

读写锁唯一的属性就是进程共享属性 设置和获得读写锁共享属性

  • pthread_rwlockattr_getpshared( .. )
  • pthread_rwlockattr_setpshared( .. )

例子

读锁(共享锁)

复制代码
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<cstdlib>

using namespace std;

pthread_rwlock_t g_rlock;

void *th_fn(void *arg)
{
    pthread_rwlock_rdlock(&g_rlock);
    cout<<"sub thread"<<endl;
    sleep(1);
    cout<<"out lock(sub)"<<endl;
    pthread_rwlock_unlock(&g_rlock);
    return (void *)10;
}

int main()
{
    pthread_t ptid;
    void *tret;

    pthread_rwlock_init(&g_rlock,NULL);

    pthread_create(&ptid, NULL,th_fn ,NULL);
    
    sleep(1);
    pthread_rwlock_rdlock(&g_rlock);
    cout<<"main thread"<<endl;
    sleep(1);
    cout<<"out lock(main)"<<endl;
    pthread_rwlock_unlock(&g_rlock);

    pthread_rwlock_destroy(&g_rlock);
    pthread_join(ptid, &tret);
    cout<<"thread code 2 exit id = "<<(int)tret<<endl;
    
    return 0;
}
复制代码

 

写锁

复制代码
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<cstdlib>

using namespace std;

pthread_rwlock_t g_rlock;

void *th_fn(void *arg)
{
    pthread_rwlock_wrlock(&g_rlock);
    cout<<"sub thread"<<endl;
    sleep(1);
    cout<<"out lock(sub)"<<endl;
    pthread_rwlock_unlock(&g_rlock);
    return (void *)10;
}

int main()
{
    pthread_t ptid;
    void *tret;

    pthread_rwlock_init(&g_rlock,NULL);

    pthread_create(&ptid, NULL,th_fn ,NULL);
    
    sleep(1);
    pthread_rwlock_wrlock(&g_rlock);
    cout<<"main thread"<<endl;
    sleep(1);
    cout<<"out lock(main)"<<endl;
    pthread_rwlock_unlock(&g_rlock);

    pthread_rwlock_destroy(&g_rlock);
    pthread_join(ptid, &tret);
    cout<<"thread code 2 exit id = "<<(int)tret<<endl;
    
    return 0;
}
复制代码

 

读些锁复用

复制代码
#include<unistd.h>
#include<cstdlib>

using namespace std;

pthread_rwlock_t g_rlock;

void *th_fn1(void *arg)
{
    cout<<"into th_fn1"<<endl;
    sleep(10);
    pthread_rwlock_rdlock(&g_rlock);
    cout<<"sub thread"<<endl;
    sleep(1);
    cout<<"out rlock(sub)"<<endl;
    pthread_rwlock_unlock(&g_rlock);
    return (void *)10;
}

void *th_fn2(void *arg)
{
    cout<<"into th_fn2"<<endl;
    sleep(1);
    pthread_rwlock_wrlock(&g_rlock);
    cout<<"sub thread"<<endl;
    sleep(1);
    cout<<"out wlock(sub)"<<endl;
    pthread_rwlock_unlock(&g_rlock);
    return (void *)10;
}

int main()
{
    pthread_t ptid1,ptid2;
    void *tret;

    pthread_rwlock_init(&g_rlock,NULL);

    pthread_create(&ptid1, NULL,th_fn1 ,NULL);
    pthread_create(&ptid2, NULL,th_fn2 ,NULL);
    
    sleep(1);
    pthread_rwlock_rdlock(&g_rlock);
    cout<<"main thread"<<endl;
    sleep(1);
    cout<<"out rlock(main)"<<endl;
    pthread_rwlock_unlock(&g_rlock);

    pthread_join(ptid1 ,&tret);
    cout<<"thread1 code 2 exit id = "<<(int)tret<<endl;
    pthread_join(ptid2 ,&tret);
    cout<<"thread2 code 2 exit id = "<<(int)tret<<endl;
    
    pthread_rwlock_destroy(&g_rlock);

    return 0;
}
复制代码


条件变量

说明

条件变量是线程可用的另一种同步机制.条件变量给多个线程提供一个会合场所.条件变量与互斥量一起使用时,允许线程以无竞争的方式等的特定的条件发生.条件变量本身就是互斥量保护的.线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前是不会察觉到这种改变,因此必须锁定互斥量才能计算条件.在调用 pthread_cond_wait 或 pthread_cond_timedwait 时候,调用者把锁住的互斥量传递给函数.函数把调用线程放到等待条件的线程列表上,然后对互斥量进行解锁,这两个操作是原子操作. pthread_cond_wait 返回时,互斥量再次被锁住.

初始化和销毁条件变量

  • pthread_cond_init( .. )
  • pthread_cond_destroy( .. )

等待条件函数

  • pthread_cond_wait( .. )
  • pthread_cond_timedwait( .. )

发送条件信号

  • pthread_cond_signal( .. )
  • pthread_cond_broadcast( .. )

条件变量初始化属性和释放属性

  • pthread_condattr_init( .. )
  • pthread_condattr_destroy( .. )

条件变量支持进程共享属性

  • pthread_condattr_getshared( .. )
  • pthread_condattr_setshared( .. )

例子

复制代码
#include "pthread.h"
#include "unistd.h"
#include <iostream>

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *thread1(void *);
void *thread2(void *);

int i=1;

int main(void)
{
    pthread_t t_a;
    pthread_t t_b;

    pthread_create(&t_a,NULL,thread2,(void *)NULL);
    pthread_create(&t_b,NULL,thread1,(void *)NULL);
    pthread_join(t_b, NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    
    return 0;
}

void *thread1(void *junk)
{
    for(i=1;i<=9;i++) 
    {
        pthread_mutex_lock(&mutex);
        if(i%3==0)
            pthread_cond_signal(&cond);
        else
            cout<<"thread_1:\t"<<i<<endl;
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

void *thread2(void *junk)
{
    while(i<9)
    {
        pthread_mutex_lock(&mutex);
        if(i%3!=0)
            pthread_cond_wait(&cond,&mutex);
        cout<<"thread_2:\t"<<i<<endl;
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}
复制代码

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值