超详细讲解Linux C++多线程同步的方式

目录
  • 一.互斥锁
    • 1.互斥锁的初始化
    • 2.互斥锁的相关属性及分类
    • 3,测试加锁函数
  • 二.条件变量
    • 1.条件变量的相关函数
    • 1)初始化的销毁读写锁
    • 2)以写的方式获取锁,以读的方式获取锁,释放读写锁
  • 四.信号量
    • 1)信号量初始化
    • 2)信号量值的加减
    • 3)对信号量进行清理

背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题?

通过多线程模拟多窗口售票为例:

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

using namespace std;

int ticket_sum=20;
void *sell_ticket(void *arg)
{
    for(int i=0; i<20; i++)
    {
        if(ticket_sum>0)
        {
            sleep(1);
            cout<<"sell the "<<20-ticket_sum+1<<"th"<<endl;
            ticket_sum--;
        }
    }
    return 0;
}

int main()
{
    int flag;
    pthread_t tids[4];

    for(int i=0; i<4; i++)
    {
        flag=pthread_create(&tids[i],NULL,&sell_ticket,NULL);
        if(flag)
        {
            cout<<"pthread create error ,flag="<<flag<<endl;
            return flag;
        }
    }

    sleep(20);
    void *ans;
    for(int i=0; i<4; i++)
    {
        flag=pthread_join(tids[i],&ans);
        if(flag)
        {
            cout<<"tid="<<tids[i]<<"join erro flag="<<flag<<endl;
            return flag;
        }
        cout<<"ans="<<ans<<endl;
    }
    return 0;
}

分析:总票数只有20张,却卖出了23张,是非常明显的超买超卖问题,而造成这个问题的根本原因就是同时发生的各个线程都可以对ticket_sum进行读取和写入!

ps:

1.在并发情况下,指令执行的先后顺序由内核决定,同一个线程内部,指令按照先后顺序执行,但不同线程之间的指令很难说清楚是哪一个先执行,如果运行的结果依赖于不同线程执行的先后的话,那么就会形成竞争条件,在这样的情况下,计算的结果很难预知,所以应该尽量避免竞争条件的形成

2.最常见的解决竞争条件的方法是将原先分离的两个指令构成一个不可分割的原子操作,而其他任务不能插入到原子操作中!

3.对多线程来说,同步指的是在一定时间内只允许某一个线程访问某个资源,而在此时间内,不允许其他线程访问该资源!

4.线程同步的常见方法:互斥锁,条件变量,读写锁,信号量

一.互斥锁

本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会锁上变成lock状态,此后只有该线程由权力打开该锁,其他线程想要获得互斥锁,必须得到互斥锁再次被打开之后

采用互斥锁来同步资源:

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

using namespace std;

int ticket_sum=20;
pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;//static init mutex

void *sell_ticket(void *arg)
{
    for(int i=0; i<20; i++)
    {
        pthread_mutex_lock(&mutex_x);//atomic opreation through mutex lock
        if(ticket_sum>0)
        {
            sleep(1);
            cout<<"sell the "<<20-ticket_sum+1<<"th"<<endl;
            ticket_sum--;
        }
        pthread_mutex_unlock(&mutex_x);
    }
    return 0;
}

int main()
{
    int flag;
    pthread_t tids[4];

    for(int i=0; i<4; i++)
    {
        flag=pthread_create(&tids[i],NULL,&sell_ticket,NULL);
        if(flag)
        {
            cout<<"pthread create error ,flag="<<flag<<endl;
            return flag;
        }
    }

    sleep(20);
    void *ans;
    for(int i=0; i<4; i++)
    {
        flag=pthread_join(tids[i],&ans);
        if(flag)
        {
            cout<<"tid="<<tids[i]<<"join erro flag="<<flag<<endl;
            return flag;
        }
        cout<<"ans="<<ans<<endl;
    }
    return 0;
}

分析:通过为售票的核心代码段加互斥锁使得其变成了一个原子性操作!不会被其他线程影响

1.互斥锁的初始化

互斥锁的初始化分为静态初始化和动态初始化

静态:pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;//static init mutex

动态:pthread_mutex_init函数

ps:互斥锁静态初始化和动态初始化的区别?

待补充。。。。

2.互斥锁的相关属性及分类

//初始化互斥锁属性
pthread_mutexattr_init(pthread_mutexattr_t attr);

//销毁互斥锁属性
pthread_mutexattr_destroy(pthread_mutexattr_t attr);

//用于获取互斥锁属性
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr , int *restrict pshared);

//用于设置互斥锁属性
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr , int pshared);

attr表示互斥锁的属性

pshared表示互斥锁的共享属性,由两种取值:

1)PTHREAD_PROCESS_PRIVATE:锁只能用于一个进程内部的两个线程进行互斥(默认情况)

2)PTHREAD_PROCESS_SHARED:锁可用于两个不同进程中的线程进行互斥,使用时还需要在进程共享内存中分配互斥锁,然后为该互斥锁指定属性就可以了

互斥锁的分类:

//获取互斥锁类型
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr , int *restrict type);

//设置互斥锁类型
int pthread_mutexattr_settype(const pthread_mutexattr_t *restrict attr , int type);

参数type表示互斥锁的类型,总共有以下四种类型:

1.PTHREAD_MUTEX_NOMAL:标准互斥锁,第一次上锁成功,第二次上锁会失败并阻塞

2.PTHREAD_MUTEX_RECURSIVE:递归互斥锁,第一次上锁成功,第二次上锁还是会成功,可以理解为内部有一个计数器,每加一次锁计数器加1,解锁减1

3.PTHREAD_MUTEX_ERRORCHECK:检查互斥锁,第一次上锁会成功,第二次上锁出错返回错误信息,不会阻塞

4.PTHREAD_MUTEX_DEFAULT:默认互斥锁,第一次上锁会成功,第二次上锁会失败

3,测试加锁函数

int pthread_mutex_lock(&mutex):测试加锁函数在锁已经被占据时返回EBUSY而不是挂起等待,当然,如果锁没有被占领的话可以获得锁

为了清楚的看到两个线程争用资源的情况,我们使得其中一个函数使用测试加锁函数进行加锁,而另外一个使用正常的加锁函数进行加锁

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

int ticket_sum=20;
pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;//static init mutex

void *sell_ticket_1(void *arg)
{
    for(int i=0; i<20; i++)
    {
        pthread_mutex_lock(&mutex_x);
        if(ticket_sum>0)
        {
            sleep(1);
            cout<<"thread_1 sell the "<<20-ticket_sum+1<<"th ticket"<<endl;
            ticket_sum--;
        }
        sleep(1);
        pthread_mutex_unlock(&mutex_x);
        sleep(1);
    }
    return 0;
}

void *sell_ticket_2(void *arg)
{
    int flag;
    for(int i=0; i<10; i++)
    {
        flag=pthread_mutex_trylock(&mutex_x);
        if(flag==EBUSY)
        {
            cout<<"sell_ticket_2:the variable is locked by sell_ticket_1"<<endl;
        }
        else if(flag==0)
        {
            if(ticket_sum>0)
            {
                sleep(1);
                cout<<"thread_2 sell the "<<20-ticket_sum+1<<"th tickets"<<endl;
                ticket_sum--;
            }
            pthread_mutex_unlock(&mutex_x);
        }
        sleep(1);
    }
    return 0;
}
int main()
{
    int flag;
    pthread_t tids[2];

    flag=pthread_create(&tids[0],NULL,&sell_ticket_1,NULL);
    if(flag)
    {
        cout<<"pthread create error ,flag="<<flag<<endl;
        return flag;
    }

    flag=pthread_create(&tids[1],NULL,&sell_ticket_2,NULL);
    if(flag)
    {
        cout<<"pthread create error ,flag="<<flag<<endl;

        return flag;
    }

    void *ans;
    sleep(30);
    flag=pthread_join(tids[0],&ans);
    if(flag)
    {
        cout<<"tid="<<tids[0]<<"join erro flag="<<flag<<endl;
        return flag;
    }
    else
    {
        cout<<"ans="<<ans<<endl;
    }

    flag=pthread_join(tids[1],&ans);
    if(flag)
    {
        cout<<"tid="<<tids[1]<<"join erro flag="<<flag<<endl;
        return flag;
    }
    else
    {
        cout<<"ans="<<ans<<endl;
    }

    return 0;
}

分析:通过测试加锁函数我们可以清晰的看到两个线程争用资源的情况

二.条件变量

互斥量不是万能的,比如某个线程正在等待共享数据内某个条件出现,可可能需要重复对数据对象加锁和解锁(轮询),但是这样轮询非常耗费时间和资源,而且效率非常低,所以互斥锁不太适合这种情况

我们需要这样一种方法:当线程在等待满足某些条件时使线程进入睡眠状态,一旦条件满足,就换线因等待满足特定条件而睡眠的线程

如果我们能够实现这样一种方法,程序的效率无疑会大大提高,而这种方法正是条件变量!

样例:

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

pthread_cond_t qready=PTHREAD_COND_INITIALIZER;   //cond
pthread_mutex_t qlock=PTHREAD_MUTEX_INITIALIZER;  //mutex

int x=10,y=20;

void *f1(void *arg)
{
  cout<<"f1 start"<<endl;
  pthread_mutex_lock(&qlock);
  while(x<y)
  {
    pthread_cond_wait(&qready,&qlock);
  }
  pthread_mutex_unlock(&qlock);
  sleep(3);
  cout<<"f1 end"<<endl;
  return 0;
}

void *f2(void *arg)
{
  cout<<"f2 start"<<endl;
  pthread_mutex_lock(&qlock);
  x=20;
  y=10;
  cout<<"has a change,x="<<x<<" y="<<y<<endl;
  pthread_mutex_unlock(&qlock);
  if(x>y)
  {
    pthread_cond_signal(&qready);
  }
  cout<<"f2 end"<<endl;
  return 0;
}

int main()
{
  pthread_t tids[2];
  int flag;

  flag=pthread_create(&tids[0],NULL,f1,NULL);
  if(flag)
  {
    cout<<"pthread 1 create error "<<endl;
    return flag;
  }

  sleep(2);

  flag=pthread_create(&tids[1],NULL,f2,NULL);
  if(flag)
  {
    cout<<"pthread 2 create erro "<<endl;
    return flag;
  }

  sleep(5);
  return 0;
}

分析:线程1不满足条件被阻塞,然后线程2运行,改变了条件,线程2发行条件改变了通知线程1运行,然后线程2结束,然后线程1继续运行,然后线程1结束,为了确保线程1先执行,在创建线程2之前我们sleep了2秒

ps:

1.条件变量通过运行线程阻塞和等待另一个线程发送信号的方法弥补互斥锁的不足,常常和互斥锁一起使用,使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开响应的互斥锁并等待条件发生变化,一旦其他的某个线程改变了条件变量,它将通知响应的条件变量换线一个或多个正被此条件变量阻塞的线程,这些线程将重新锁定互斥锁并且重新测试条件是否满足

1.条件变量的相关函数

1)创建

静态方式:pthread_cond_t cond PTHREAD_COND_INITIALIZER

动态方式:int pthread_cond_init(&cond,NULL)

Linux thread 实现的条件变量不支持属性,所以NULL(cond_attr参数)

2)注销

int pthread_cond_destory(&cond)

只有没有线程在该条件变量上,该条件变量才能注销,否则返回EBUSY

因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程!(请参考条件变量的底层实现)

3)等待

条件等待:int pthread_cond_wait(&cond,&mutex)

计时等待:int pthread_cond_timewait(&cond,&mutex,time)

1.其中计时等待如果在给定时刻前条件没有被满足,则返回ETIMEOUT,结束等待

2.无论那种等待方式,都必须有一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait形成竞争条件!

3.在调用pthread_cond_wait前必须由本线程加锁

4)激发

激发一个等待线程:pthread_cond_signal(&cond)

激发所有等待线程:pthread_cond_broadcast(&cond)

重要的是,pthread_cond_signal不会存在惊群效应,也就是是它最多给一个等待线程发信号,不会给所有线程发信号唤醒提他们,然后要求他们自己去争抢资源!

pthread_cond_signal会根据等待线程的优先级和等待时间来确定激发哪一个等待线程

下面看一个程序,找到程序存在的问题

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

pthread_cond_t taxi_cond=PTHREAD_COND_INITIALIZER; //taix arrive cond
pthread_mutex_t taxi_mutex=PTHREAD_MUTEX_INITIALIZER;// sync mutex

void *traveler_arrive(void *name)
{
    cout<<"Traveler:"<<(char*)name<<" needs a taxi now!"<<endl;
    pthread_mutex_lock(&taxi_mutex);
    pthread_cond_wait(&taxi_cond,&taxi_mutex);
    pthread_mutex_unlock(&taxi_mutex);
    cout<<"Traveler:"<<(char*)name<<" now got a taxi!"<<endl;
    pthread_exit((void*)0);
}

void *taxi_arrive(void *name)
{
    cout<<"Taxi:"<<(char*)name<<" arriver."<<endl;
    pthread_cond_signal(&taxi_cond);
    pthread_exit((void*)0);
}

int main()
{
    pthread_t tids[3];
    int flag;

    flag=pthread_create(&tids[0],NULL,taxi_arrive,(void*)("Jack"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    flag=pthread_create(&tids[1],NULL,traveler_arrive,(void*)("Susan"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    flag=pthread_create(&tids[2],NULL,taxi_arrive,(void*)("Mike"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    void *ans;
    for(int i=0; i<3; i++)
    {
        flag=pthread_join(tids[i],&ans);
        if(flag)
        {
            cout<<"pthread_join error:flag="<<flag<<endl;
            return flag;
        }
        cout<<"ans="<<ans<<endl;
    }
    return 0;
}

分析:程序由一个条件变量,用于提示乘客有出租车到达,还有一个同步锁,乘客到达之后就是等车(条件变量),出租车到达之后就是通知乘客,我们看到乘客Susan到达之后,并没有乘坐先到的Jack的车,而是等到Mike的车到了之后再乘坐Mike的车,Jack的车白白的闲置了,为什么会造成这种原因呢?分析一下代码:我们发现Jack出租车到达之后调用pthread_cond_signal(&taxi_cond)发现没有乘客,然后就直接结束线程了。。。。

正确的操作应该是:先到的Jack发现没有乘客,然后一直等待乘客,有乘客到了就直接走,而且我们应该统计一下乘客的数量

做如下改进:

1.增加乘客计数器,使得出租车在有乘客到达之后可以直接走,而不是又在原地等待别的乘客(僵死线程)

2.出租车到达函数加个while循环,没有乘客的时候一直等待,直到乘客到来

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

pthread_cond_t taxi_cond=PTHREAD_COND_INITIALIZER; //taix arrive cond
pthread_mutex_t taxi_mutex=PTHREAD_MUTEX_INITIALIZER;// sync mutex

void *traveler_arrive(void *name)
{
    cout<<"Traveler:"<<(char*)name<<" needs a taxi now!"<<endl;
    pthread_mutex_lock(&taxi_mutex);

    pthread_cond_wait(&taxi_cond,&taxi_mutex);
    pthread_mutex_unlock(&taxi_mutex);
    cout<<"Traveler:"<<(char*)name<<" now got a taxi!"<<endl;
    pthread_exit((void*)0);
}

void *taxi_arrive(void *name)
{
    cout<<"Taxi:"<<(char*)name<<" arriver."<<endl;

    pthread_exit((void*)0);
}

int main()
{
    pthread_t tids[3];
    int flag;

    flag=pthread_create(&tids[0],NULL,taxi_arrive,(void*)("Jack"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    flag=pthread_create(&tids[1],NULL,traveler_arrive,(void*)("Susan"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    flag=pthread_create(&tids[2],NULL,taxi_arrive,(void*)("Mike"));
    if(flag)
    {
        cout<<"pthread_create error:flag="<<flag<<endl;
        return flag;
    }
    cout<<"time passing by"<<endl;
    sleep(1);

    void *ans;
    for(int i=0; i<3; i++)
    {
        flag=pthread_join(tids[i],&ans);
        if(flag)
        {
            cout<<"pthread_join error:flag="<<flag<<endl;
            return flag;
        }
        cout<<"ans="<<ans<<endl;
    }
    return 0;
}

三.读写锁

可以多个线程同时读,但是不能多个线程同时写

1.读写锁比互斥锁更加具有适用性和并行性

2.读写锁最适用于对数据结构的读操作读操作次数多余写操作次数的场合!

3.锁处于读模式时可以线程共享,而锁处于写模式时只能独占,所以读写锁又叫做共享-独占锁

4.读写锁有两种策略:强读同步和强写同步

在强读同步中,总是给读者更高的优先权,只要写者没有进行写操作,读者就可以获得访问权限

在强写同步中,总是给写者更高的优先权,读者只能等到所有正在等待或者执行的写者完成后才能进行读

不同的系统采用不同的策略,比如航班订票系统使用强写同步,图书馆查阅系统采用强读同步

根据不同的业务场景,采用不同的策略

1)初始化的销毁读写锁

静态初始化:pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER

动态初始化:int pthread_rwlock_init(rwlock,NULL),NULL代表读写锁采用默认属性

销毁读写锁:int pthread_rwlock_destory(rwlock)

在释放某个读写锁的资源之前,需要先通过pthread_rwlock_destory函数对读写锁进行清理。释放由pthread_rwlock_init函数分配的资源

如果你想要读写锁使用非默认属性,则attr不能为NULL,得给attr赋值

int pthread_rwlockattr_init(attr),给attr初始化

int pthread_rwlockattr_destory(attr),销毁attr

2)以写的方式获取锁,以读的方式获取锁,释放读写锁

int pthread_rwlock_rdlock(rwlock),以读的方式获取锁

int pthread_rwlock_wrlock(rwlock),以写的方式获取锁

int pthread_rwlock_unlock(rwlock),释放锁

上面两个获取锁的方式都是阻塞的函数,也就是说获取不到锁的话,调用线程不是立即返回,而是阻塞执行,在需要进行写操作的时候,这种阻塞式获取锁的方式是非常不好的,你想一下,我需要进行写操作,不但没有获取到锁,我还一直在这里等待,大大拖累效率

所以我们应该采用非阻塞的方式获取锁:

int pthread_rwlock_tryrdlock(rwlock)

int pthread_rwlock_trywrlock(rwlock)

读写锁的样例:

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

int num=5;
pthread_rwlock_t rwlock;

void *reader(void *arg)
{
  pthread_rwlock_rdlock(&rwlock);
  cout<<"reader "<<(long)arg<<" got the lock"<<endl;
  pthread_rwlock_unlock(&rwlock);
  return 0;
}

void *writer(void *arg)
{
  pthread_rwlock_wrlock(&rwlock);
  cout<<"writer "<<(long)arg<<" got the lock"<<endl;
  pthread_rwlock_unlock(&rwlock);
  return 0;
}

int main()
{
  int flag;
  long n=1,m=1;
  pthread_t wid,rid;
  pthread_attr_t attr;

  flag=pthread_rwlock_init(&rwlock,NULL);
  if(flag)
  {
    cout<<"rwlock init error"<<endl;
    return flag;
  }

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//thread sepatate

  for(int i=0;i<num;i++)
  {
    if(i%3)
    {
      pthread_create(&rid,&attr,reader,(void *)n);
      cout<<"create reader "<<n<<endl;
      n++;
    }else
    {
      pthread_create(&wid,&attr,writer,(void *)m);
      cout<<"create writer "<<m<<endl;
      m++;
    }
  }

  sleep(5);//wait other done
  return 0;
}

分析:3个读线程,2个写线程,读线程比写线程多

当读写锁是写状态时,在锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞

当读写锁是读状态时,在锁被解锁之前,所有视图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程会被阻塞

所以读写锁默认是强读模式!

四.信号量

信号量(sem)和互斥锁的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程进入临界区

1)信号量初始化

int sem_init(&sem,pshared,v)

pshared为0表示这个信号量是当前进程的局部信号量

pshared为1表示这个信号量可以在多个进程之间共享

v为信号量的初始值

成功返回0,失败返回-1

2)信号量值的加减

int sem_wait(&sem):以原子操作的方式将信号量的值减去1

int sem_post(&sem):以原子操作的方式将信号量的值加上1

3)对信号量进行清理

int sem_destory(&sem)

通过信号量模拟2个窗口,10个客人进行服务的过程

样例:

#include <iostream>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<semaphore.h>
using namespace std;

int num=10;
sem_t sem;

void *get_service(void *cid)
{
  int id=*((int*)cid);
  if(sem_wait(&sem)==0)
  {
     sleep(5);
     cout<<"customer "<<id<<" get the service"<<endl;
     cout<<"customer "<<id<<" done "<<endl;
     sem_post(&sem);
  }
  return 0;
}

int main()
{
  sem_init(&sem,0,2);
  pthread_t customer[num];
  int flag;

  for(int i=0;i<num;i++)
  {
    int id=i;
    flag=pthread_create(&customer[i],NULL,get_service,&id);
    if(flag)
    {
      cout<<"pthread create error"<<endl;
      return flag;
    }else
    {
      cout<<"customer "<<i<<" arrived "<<endl;
    }
    sleep(1);
  }

  //wait all thread done
  for(int j=0;j<num;j++)
  {
    pthread_join(customer[j],NULL);
  }
  sem_destroy(&sem);
  return 0;
}

分析:信号量的值代表空闲的服务窗口,每个窗口一次只能服务一个人,有空闲窗口,开始服务前,信号量-1,服务完成后信号量+1

总结完毕:Linux c++线程同步的四种方式:互斥锁,条件变量,读写锁,信号量

到此这篇关于超详细讲解Linux C++多线程同步的方式的文章就介绍到这了,更多相关Linux C++多线程同步内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 如何实现多线程与线程同步

    CreateThread 实现多线程: 先来创建一个简单的多线程实例,无参数传递版,运行实例会发现,主线程与子线程运行无规律. #include <windows.h> #include <iostream> using namespace std; DWORD WINAPI Func(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { cout << "thread function" &l

  • C++ SOCKET多线程实现聊天小程序

    本文实例为大家分享了C++ SOCKET多线程实现聊天小程序的具体代码,供大家参考,具体内容如下 TCP/IP协议与SOCKET 什么是网络协议? 计算机网络中,各个实体之间的数据交换必须遵守事先约定好的规则,这些规则就称为协议. 网络协议的组成要素有: 1.语法,数据与控制信息的结构或格式 2.语义:需要发出何种控制信息,完成哪些动作以及做出何种响应 3.时序:事件实现顺序的详细说明 在一个网络协议中,通信的实体的相同层次的结构必须执行相同的协议,这是协议的对等性原则. TCP/IP体系结构与

  • C++多线程中的锁和条件变量使用教程

    在做多线程编程时,有两个场景我们都会遇到: 多线程访问共享资源,需要用到锁: 多线程间的状态同步,这个可用的机制很多,条件变量是广泛使用的一种. 今天我用一个简单的例子来给大家介绍下锁和条件变量的使用. 代码使用C++11 示例代码 #include <iostream> #include <mutex> #include <thread> #include <condition_variable> std::mutex g_mutex; // 用到的全局锁

  • C++多线程实现TCP服务器端同时和多个客户端通信

    通讯建立后首先由服务器端发送消息,客户端接收消息:接着客户端发送消息,服务器端接收消息,实现交互发送消息. 服务器同时可以和多个客户端建立连接,进行交互: 在某次交互中,服务器端或某客户端有一方发送"end"即终止服务器与其的通信:服务器还可以继续接收其他客户端的请求,与其他客户端通信. 服务器端 #include <WinSock2.h> #include <WS2tcpip.h> #include <iostream> using namespa

  • C++11并发编程:多线程std::thread

    一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语言层面的thread可以解决这个问题. 所需头文件<thread> 二:构造函数 1.默认构造函数 thread() noexcept 一个空的std::thread执行对象 2.初始化构造函数 template<class Fn, class... Args> explicit th

  • 超详细讲解Linux C++多线程同步的方式

    目录 一.互斥锁 1.互斥锁的初始化 2.互斥锁的相关属性及分类 3,测试加锁函数 二.条件变量 1.条件变量的相关函数 1)初始化的销毁读写锁 2)以写的方式获取锁,以读的方式获取锁,释放读写锁 四.信号量 1)信号量初始化 2)信号量值的加减 3)对信号量进行清理 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通过多线程模拟多窗口售票为例: #include <iostream> #include<pthread.h> #include<stdio.h&

  • 超详细讲解Linux DHCP服务

    目录 一.DHCP服务(动态主机配置协议) 1.背景 2.概述 3.优点 4.DHCP报文类型 5.DHCP 的分配方式 二.安装 DHCP 服务器 1.DHCP 服务软件 2.主配置文件 三.配置步骤 1.使用 DHCP 动态的给 PC 机分配 IP 地址 ① eNSP ②虚拟机 ③验证 ④进入命令行"ipconfig"测试 一.DHCP服务(动态主机配置协议) 1.背景 1.手动设置工作量大且容易冲突 2.用DHCP可以减少工作量和避免地址冲突 2.概述 作用:为局域网内的电脑分配

  • Java超详细讲解多线程中的Process与Thread

    目录 进程和线程的关系 操作系统是如何管理进程的 并行和并发 创建线程的方法 串行执行和并发执行 Thread中的一次额重要方法 中断线程 线程等待 线程休眠(sleep) 进程和线程的关系 在操作系统中运行的程序就是进程,比如说QQ,播放器,游戏等等…程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念. 进程和线程都是为了处理并发编程这样的场景,但是进程有问题,频繁拆功创建和释放资源的时候效率低,相比之下,线程更轻量,创建和释放效率更高. 进程具有独立性,每个进程有各自独立

  • Python多进程并发与同步机制超详细讲解

    目录 多进程 僵尸进程 Process类 函数方式 继承方式 同步机制 状态管理Managers 在<多线程与同步>中介绍了多线程及存在的问题,而通过使用多进程而非线程可有效地绕过全局解释器锁. 因此,通过multiprocessing模块可充分地利用多核CPU的资源. 多进程 多进程是通过multiprocessing包来实现的,multiprocessing.Process对象(和多线程的threading.Thread类似)用来创建一个进程对象: 在类UNIX平台上,需要对每个Proce

  • Netty与NIO超详细讲解

    目录 Linux下的五种I/O模型 阻塞IO的流程 IO复用 信号驱动I/O 异步IO NIO I0多路复用 NIO核心组件 使用Java原生API实现NIO操作 Redis为什么支持高并发 Linux下的五种I/O模型 1)阻塞I/O(blocking I/O) 2)非阻塞I/O (nonblocking I/O) 3) I/O复用(select 和poll) (I/O multiplexing) 4)信号驱动I/O (signal driven I/O (SIGIO)) 5)异步I/O (a

  • php超详细讲解命名管道

    目录 进程间为什么要通信 进程如何实现通信 常见进程通信方式 管道概念 命名管道实现 posix_mkfifo函数 无血缘进程间通信 进程间为什么要通信 进程间通信的目的: 数据传输:一个 进程需要将它的数据 发送给另一个进程. 通知事件:一个进程需要向另一个或一组进程 发送消息,通知它(它们)发生了 某种事件(如进程终止时要通知父进程). 资源共享:多个进程之间 共享同样的资源 .为了做到这一点,需要内核提供互斥和同步机制. 进程控制:有些进程 希望完全控制另一个进程的执行 (如 Debug

  • C++ Boost log日志库超详细讲解

    目录 一.说明 二.库Boost.Log 一.说明 应用程序库是指通常专门用于独立应用程序开发而不用于库开发的库. Boost.Log 是一个日志库. Boost.ProgramOptions 是一个用于定义和解析命令行选项的库. Boost.Serialization 允许您序列化对象,例如,将它们保存到文件或从文件加载它们. Boost.Uuid 支持使用 UUID. 具体内容 62. Boost.Log 63. Boost.ProgramOptions 64. Boost.Serializ

  • Java 超详细讲解设计模式之中的建造者模式

    目录 1.什么是建造者模式? 2.建造者模式的定义 3.建造者模式的优缺点 4.建造者模式的结构 5.建造者模式代码演示 6.建造者模式的应用场景 7.建造者模式和工厂模式的区别 1.什么是建造者模式? 我们知道在软件开发过程中有时需要创建一个很复杂的对象,通常由多个子部件按一定的步骤组合而成. 例如,比如我们在自己在组装一台计算机的时候,需要有 CPU.主板.内存.硬盘.显卡.机箱.显示器.键盘.鼠标等部件组装而成的.比如学校需要采购100台计算机,学校不可能自己把零件买过来自己组装,肯定是告

  • Redis超详细讲解高可用主从复制基础与哨兵模式方案

    目录 高可用基础---主从复制 主从复制的原理 主从复制配置 示例 1.创建Redis实例 2.连接数据库并设置主从复制 高可用方案---哨兵模式sentinel 哨兵模式简介 哨兵工作原理 哨兵故障修复原理 sentinel.conf配置讲解 哨兵模式的优点 哨兵模式的缺点 高可用基础---主从复制 Redis的复制功能是支持将多个数据库之间进行数据同步,主数据库可以进行读写操作.当主数据库数据发生改变时会自动同步到从数据库,从数据库一般是只读的,会接收注数据库同步过来的数据. 一个主数据库可

  • C语言 struct结构体超详细讲解

    目录 一.本章重点 二.创建结构体 三.typedef与结构体的渊源 四.匿名结构体 五.结构体大小 六.结构体指针 七.其他 一.本章重点 创建结构体 typedef与结构体的渊源 匿名结构体 结构体大小 结构体指针 其他 二.创建结构体 先来个简单的结构体创建 这就是一个比较标准的结构体 struct people { int age; int id; char address[10]; char sex[5]; };//不要少了分号. 需要注意的是不要少了分号. 那么这样创建结构体呢? s

随机推荐