Linux下的多线程编程二(线程的同步与互斥)

Linux下的多线程编程一(线程的基础知识)

一、什么叫做线程的同步与互斥?为什么需要同步与互斥?

1、同步与互斥
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。

2、线程的同步与异步
线程同步是多个线程同时访问同一资源,等待资源访问结束,浪费时间,效率低 。
线程异步:访问资源时在空闲等待时同时访问其他资源,实现多线程机制 。异步处理就是,你现在问我问题,我可以不回答你,等我用时间了再处理你这个问题。

同步信息被立即处理 – 直到信息处理完成才返回消息句柄;
异步信息收到后将在后台处理一段时间 – 而早在信息处理结束前就返回消息句柄。

3、多线程的同步与互斥的区别
假如把整条道路看成是一个【进程】的话,那么马路中间白色虚线分隔开来的各个车道就是进程中的各个【线程】了。
①这些线程(车道)共享了进程(道路)的公共资源(土地资源)。
②这些线程(车道)必须依赖于进程(道路),也就是说,线程不能脱离于进程而存在(就像离开了道路,车道也就没有意义了)。
③这些线程(车道)之间可以并发执行(各个车道你走你的,我走我的),也可以互相同步(某些车道在交通灯亮时禁止继续前行或转弯,必须等待其它车道的车辆通行完毕)。
④这些线程(车道)之间依靠代码逻辑(交通灯)来控制运行,一旦代码逻辑控制有误(死锁,多个线程同时竞争唯一资源),那么线程将陷入混乱,无序之中。
⑤这些线程(车道)之间谁先运行是未知的,只有在线程刚好被分配到CPU时间片(交通灯变化)的那一刻才能知道。

注:由于用于互斥的信号量sem与所有的并发进程有关,所以称之为公有信号量。公有信号量的值反映了公有资源的数量。只要把临界区置于P(sem)和V(sem)之间,即可实现进程间的互斥。就象火车中的每节车厢只有一个卫生间,该车厢的所有旅客共享这个公有资源:卫生间,所以旅客间必须互斥进入卫生间,只要把卫生间放在P(sem)和V(sem)之间,就可以到达互斥的效果。

二、mutex(互斥量)

1、多线程的冲突举例
我们知道,多个线程同时访问共享数据时,可能会产生冲突。比如两个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成:
1> 从内存读变量值到寄存器
2>寄存器的值加1
3>将寄存器的值写回内存
假设两个线程在多处理器平台上同时执行这三条指令,则可能导致下图所示的结果,最后变量只加了一次而非两次。
这里写图片描述

数据二义性产生原因:不同的线程访问同一份临界资源,其次线程的操作不是原子操作。
为了验证多线程同时访问临界资源产生的冲突与数据的二义性,创建两个线程,各自把global增加5000次,正常情况下最后global应该等于10000,但事实上每次运行该程序的结果都不一样,代码如下(为了使现象更容易观察到,我们把上述三条指令做的事情用更多条指令来做):

  1 /************************************** 
  2 *文件说明:mutex.c 
  3 *作者:段晓雪 
  4 *创建时间:2017年05月29日 星期一 16时11分42秒 
  5 *开发环境:Kali Linux/g++ v6.3.0 
  6 ****************************************/ 
  7 #include<stdio.h> 
  8 #include<stdlib.h> 
  9 #include<pthread.h> 
 10  
 11 int global = 0;//定义全局变量 
 13 {
 14     int i = 5000;//数据太小,线程发生冲突的概率比较小
 15     //int i = 5000000;//1、增大循环次数,发生冲突较多
 16     //while(i--)
 17     //{
   
 18     //  global++;
 19     //}
 20     //2、增多线程切换:内核态---->用户态,增大冲突概率
 21     while(i--)
 22     {
 23         int tmp = global;
 24         printf("%d\n",global);
 25         global = tmp + 1;
 26     }
 27     //printf("\n");
 28     return (void*)0;
 29 }
 30 int main()
 31 {
 32     pthread_t tid1;
 33     pthread_t tid2;
 34     pthread_create(&tid1,NULL,pthread_add,NULL);//创建线程1
 35     pthread_create(&tid2,NULL,pthread_add,NULL);//创建线程2
 36     pthread_join(tid1,NULL);//等待线程1
 37     pthread_join(tid2,NULL);//等待线程2
 38     printf("the global is %d\n",global);
 39     return 0;
 40 }

第一次运行结果—-5101:
这里写图片描述

第二次运行结果—-5091:
这里写图片描述

第三次运行结果—-5000:
这里写图片描述

3次运行结果均不一样,足以说明多线程在同步访问全局变量global时发生了冲突。

2、解决多线程的冲突问题 —-引入互斥锁
(1)互斥锁:(Mutex,Mutual Exclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得 锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。

(2)互斥锁的创建和销毁:
Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁。
1>定义一把锁

pthread_mutex_t lock;

2>互斥锁lock的初始化
如果lock是局部变量—可用初始化函数pthread_mutex_init初始化。

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

返回值:成功返回0,失败返回错误号。

如果lock是静态或全局变量—可用 PTHREAD_MUTEX_INITIALIZER初始化 ,相当于用pthread_mutex_init初始化并且attr参数为NULL。

3>加锁与解锁

int pthread_mutex_trylock(pthread_mutex_t* mutex);//非阻塞式加锁
int pthread_mutex_lock(pthread_mutex_t* mutex)//阻塞式加锁
int pthread_mutex_unlock(pthread_mutex_t* mutex)//解锁

如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被 另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。
Mutex的两个基本操作lock和unlock是如何实现的呢?
假设Mutex变量 的值为1表示互斥锁空闲,这时某个进程调用lock可以获得锁,而Mutex的值为0表示互斥锁已经被某个线程获得,其它线程再调用lock只能挂起等待。

4>销毁锁资源

int pthread_mutex_destroy(pthread_mutex_t* mutex);

返回值:成功返回0,失败返回错误号。

(3)解决多线程的冲突问题

   #include<stdio.h>
   #include<stdlib.h>
   #include<pthread.h>

   int 
  • 3
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux下的多线程编程可以使用C/C++语言实现。C/C++语言提供了一些多线程编程的库,如pthread库、OpenMP库、Boost库等。其,pthread库是Linux下最常用的多线程编程库,它提供了一系列的API函数,可以用来创建、管理和同步线程。在C/C++语言,可以使用pthread_create()函数创建线程,使用pthread_join()函数等待线程结束,使用pthread_mutex_lock()和pthread_mutex_unlock()函数实现线程间的互斥访问等。同时,C++11标准也提供了一些多线程编程的支持,如std::thread类、std::mutex类等,可以方便地实现多线程编程。 ### 回答2: Linux下的多线程编程是指在Linux系统下使用多个线程来执行不同的任务,从而提高程序的运行效率和响应速度。 C/C++是Linux下最常用的编程语言之一,也是多线程编程的主要语言。实现多线程编程可以使用线程库,其最常用的是pthread库。 Pthread库是Linux下的开放式多线程库,它允许程序员使用标准的POSIX线程接口来创建、终止、同步和管理线程。使用Pthread库可以很方便地进行多线程编程,其主要包括以下几个方面。 1. 创建和启动线程:使用pthread_create函数来创建和启动线程,该函数需要传递线程ID、线程属性和线程函数等参数。 2. 同步线程:使用pthread_join函数来等待一个线程结束,以便获取线程的返回值。使用pthread_mutex和pthread_cond等函数来进行线程同步。 3. 线程控制:使用pthread_cancel函数来取消线程,使用pthread_exit函数来终止线程。 4. 共享变量:在多个线程之间共享变量时,需要使用pthread_mutex和pthread_cond等函数来控制并发访问。 在进行多线程编程时,需要注意一些问题,如线程安全、死锁等问题。不同的线程对共享资源的读写需要使用同步机制,避免竞争和冲突。此外,要注意避免死锁,即多个线程互相等待对方释放资源,造成程序无法正常运行。 总之,Linux下的多线程编程是一项非常重要的技术,在实际开发应用广泛。使用C/C++编写多线程程序,需要熟悉线程库的使用方法,掌握线程的创建、同步、控制和共享等技术,以保证程序的稳定性和运行效率。 ### 回答3: Linux是一种开源的操作系统,其多线程编程能力是其强大之处之一。当我们需要编写一个高性能、高并发的程序时,多线程编程无疑会是一个很好的选择。 在Linux下,C/C++是最常用的编程语言之一,也是多线程编程的重要语言之一。在C/C++编写多线程程序主要依赖于pthread库。pthread库提供了一套多线程API,可以很方便的创建和管理线程。 使用pthread库创建线程需要以下步骤: 1. 包含pthread库头文件: #include <pthread.h> 2. 定义线程函数: void *thread_func(void *arg){ //线程执行的代码 } 3. 创建线程: pthread_t tid; pthread_create(&tid, NULL, thread_func, NULL); 4. 等待线程结束: pthread_join(tid, NULL); 以上代码片段就创建了一个新线程,并在新线程执行了thread_func函数。pthread_create函数的第一个参数为线程ID,第个参数为线程属性,一般使用NULL,第三个参数为线程函数,第四个参数为线程函数的参数。 多线程编程需要注意以下几点: 1. 线程安全:多个线程同时操作同一个共享资源,需要确保操作的正确性和安全性。 2. 线程同步:使用锁、互斥量等机制保证线程之间同步。 3. 线程调度:多个线程之间需要进行调度,需要注意线程优先级的设置。 总之,在Linux下使用C/C++进行多线程编程是一项非常有用的技能。在实际开发,需要结合具体的场景和需求,通过选择合适的多线程编程模型、算法和数据结构来实现高效、高性能的程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值