day 31 线程间的同步和互斥

1、进程线程之间的区别:

进程和线程的根本区别是进程是操作系统(OS)资源分配的基本单位,而线程是处理器(CPU)任务调度和执行的基本单位

地址空间:线程共享本进程的地址空间,而进程之间是独立的地址空间。

资源:线程共享本进程的资源如内存、I/O、cpu等,不利于资源的管理和保护,而进程之间的资源是独立的,能很好的进行资源管理和保护。

健壮性:多进程要比多线程健壮,一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。

执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口,执行开销大。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,执行开销小。

可并发性:两者均可并发执行。

任务切换时:进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。

 2、同步和异步通信

同步通信:面向比特流的通信发送方和接收方在同一时刻进行数据传输。为了实现这一点,通常需要一个时钟信号来协调数据的发送和接收。常见的同步通信协议包括I2C和SPI。时序一致性,响应确认,高传输速率。

异步通信:面向字节(8位)的通信,发送方和接收方之间没有严格的时序要求,它们可以独立进行操作,而无需等待对方的响应。数据一旦在发送方准备好,就可以立即发送,接收方在收到数据后进行处理。常见的异步通信协议包括UART。无时钟信号,适合长距离通信和不规则数据传输,潜在的延迟和速率。

3、阻塞IO和非阻塞IO

阻塞IO:内核IO操作彻底完成后,才返回到用户空间执行用户的操作。阻塞是指用户空间的执行状态。

非阻塞IO:用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户操作,即处于非阻塞IO状态,内核空间会立即返回给用户一个状态值。

阻塞IO:调用线程一直在等待,不能干别的事情。

非阻塞IO:调用线程拿到内核返回的状态值后,IO操作能执行就执行,不能执行就执行别的线程。

4、并发和并行

并发:系统在同一时间段内处理多个任务的能力。并发关注的是任务之间的交替执行,任务之间可能并不真正同时运行,而是通过任务的分时调度机制,使得多个任务在时间上交错进行,从而给用户一种“同时”执行的感觉

并行:系统同时执行多个任务的能力。并行是指在多个处理器或者多核处理器上,真正同时地运行多个任务。并行化的目标是提高程序的执行效率,特别是在需要处理大量数据或计算密集型任务时,并行化可以显著减少任务的完成时间。

5、什么是死锁,如何避免死锁?

死锁是指在多线程多进程环境中,两个或多个进程(或线程)互相持有对方所需资源,导致它们都无法继续执行的一种状态。

避免嵌套锁:尽量避免在持有一个锁的时候去申请另一个锁,因为这样可能会导致死锁的发生。统一获取锁的顺序:如果多个线程需要获取多个锁,可以约定获取锁的顺序,这样可以避免出现死锁。设置超时时间:在获取锁的时候可以设置超时时间,如果超过一定时间还没有获取到锁,就放弃获取锁并释放已经获取的锁。死锁检测:通过检测程序的锁状态来判断是否存在死锁,并进行相应的处理。减少锁的持有时间:尽量减少持有锁的时间,使得锁的争用时间变短,从而减少死锁的发生概率。

6、进程间的通信方式有哪些

无名管道:亲缘进程通信

有名管道:任意进程通信

信号:用户可以给某个进程发送信号,一个进程也能给另一个进程发送信号,内核也可以给某个进程发送信号,信号三种默认操作:默认,捕获,忽略。

消息队列:多个进程可同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送到队列尾部,接受进程从消息队列头部读取消息,消息一旦被读出就从队列中删除

共享内存:共享内存是将物理空间的一片区域映射到内核空间,在将映射的内核空间与用户空间的区域进行连接。共享内存的操作不是一次性的,当共享内存段中的数据被读取后,依然存在。共享内存是所有进程间通信方式中效率最高的,原因是,操作共享内存段时,无需进行用户空间和内核空间的切换。

信号灯集:信号号灯集中的每一个灯,都控制一个进程信号灯集中的等的编号都是从0开始,信号灯集中的每个灯都维护了一个value值,当value为0时,处于阻塞状态,等待其他进程将该资源的值加到1。

套接字:内核提供的函数,用于创建一个套接字,并返回当前套接字的文件描述符,通过套接字文件描述符可以实现多个进程之间的通信,可以通过文件描述符进程进程之间的绑定。

线程间的同步互斥

1、线程可以通过全局变量和局部变量实现数据通信。

2、进程运行空间都是独立的,不能进行数据通信

3、多线程间数据共享有可能执行同一个动作,导致数据异常,所以要加上互斥锁。

4、多线程除了互斥之外还有同步,也就是一个线程完成任务后,交给第二个线程完成。

5、线程访问的全局变量的资源,全局变量属于临界资源。

6、访问临界资源的代码属于临界区。

7、多个线程访问临界资源时无法确定谁先访问谁后访问,属于竞态。

线程互斥之互斥锁

1、一个线程执行任务时其他线程处于等待状态,执行任务后第二个线程再进来执行,但是执行不分先后顺序。

2、互斥锁本质上也是一个临界资源,线程获取到锁资源后执行。

3、定义和初始化互斥锁,线程上锁,解锁,销毁锁资源。

4、临界资源实质上是一个全局变量,所有的线程都共享这个资源。

#include <pthread.h>

       pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;静态初始化快速锁

       pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;静态初始化递归锁

       pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;静态初始化差错锁

       int pthread_mutex_init(pthread_mutex_t *mutex, const  pthread_mutexattr_t
       *mutexattr);
       概念:动态初始化互斥锁。
       参数1:互斥锁地址
       参数2:互斥锁属性,一般默认属性填NULL
       返回值:返回0,不会失败。

       int pthread_mutex_lock(pthread_mutex_t *mutex);
       功能:给临界区(访问临界资源的代码)上锁
       参数:互斥锁地址
       返回值:成功返回0,失败返回非0错误码。

     

       int pthread_mutex_unlock(pthread_mutex_t *mutex);
       功能:给临界区(访问临界资源的代码)解锁
       参数:互斥锁地址
       返回值:成功返回0,失败返回非0错误码。

   

       int pthread_mutex_destroy(pthread_mutex_t *mutex);
       功能:销毁锁资源
       参数:互斥锁地址
       返回值:成功返回0,失败返回非0错误码。

 

线程产生的竞态现象:

#include <myhead.h>
//创建10个线程,每个线程都对一个变量多次+1操作
#define MAX 10
#define N 500000
int k =0;
void *fun()
{
    int i;
    for(i = 0;i<N;i++)//每个线程都对变量k操作
    {
        k+=1;    
    }
}
int main(int argc, const char *argv[])
{
    pthread_t tid[MAX];
    for(int i = 0;i<MAX;i++)
    {
        if(pthread_create(&tid[i],NULL,fun,NULL)==-1)//创建10和线程1个线程体函数
        {
            perror("pthread_create");
            return -1;
        }
    }
    sleep(5);
    printf("预期结果:5000000\n");//得到的实际结果不是预期结果产生了竟态
    printf("实际结果:%d\n",k);
    return 0;
}

加入静态互斥锁解决竞态

#include <myhead.h>
//创建10个线程,每个线程都对一个变量多次+1操作
#define MAX 10
#define N 500000
int k =0;
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;//1、定义并初始化静态互斥锁

void *fun()
{
    pthread_mutex_lock(&fastmutex);//2、临界区上锁
    int i;
    for(i = 0;i<N;i++)//每个线程都对变量k操作
    {
        k+=1;    
    }
    pthread_mutex_unlock(&fastmutex);//3、临界区解锁
}
int main(int argc, const char *argv[])
{
    pthread_t tid[MAX];
    for(int i = 0;i<MAX;i++)
    {
        if(pthread_create(&tid[i],NULL,fun,NULL)==-1)//创建10和线程1个线程体函数
        {
            perror("pthread_create");
            return -1;
        }
    }
    sleep(5);

    printf("预期结果:5000000\n");//得到的实际结果不是预期结果产生了竟态
    printf("实际结果:%d\n",k);
    return 0;
}

加入:动态互斥锁解决静态

#include <myhead.h>
//创建10个线程,每个线程都对一个变量多次+1操作
#define MAX 10
#define N 500000
int k = 0;
pthread_mutex_t fastmutex;//1、定义互斥锁

void *fun()
{
    pthread_mutex_lock(&fastmutex);//3、临界区上锁
    int i;
    for(i = 0;i<N;i++)//每个线程都对变量k操作
    {
        k+=1;    
    }
    pthread_mutex_unlock(&fastmutex);//4、临界区解锁
}
int main(int argc, const char *argv[])
{
    pthread_mutex_init(&fastmutex,NULL);//2、动态初始化互斥锁
    pthread_t tid[MAX];
    for(int i = 0;i<MAX;i++)
    {
        if(pthread_create(&tid[i],NULL,fun,NULL)==-1)//创建10和线程1个线程体函数
        {
            perror("pthread_create");
            return -1;
        }
    }
    sleep(5);
    printf("预期结果:5000000\n");//得到的实际结果不是预期结果产生了竟态
    printf("实际结果:%d\n",k);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值