1 为什么要使用多线程
在编写代码时,是否会遇到以下的场景会感觉到难以下手? 要做 2 件事,一件需要阻塞等待,另一件需要实时进行。例如播放器:一边 在屏幕上播放视频,一边在等待用户的按键操作。如果使用单线程的话,程序必 须一会查询有无按键,一会播放视频。查询按键太久,就会导致视频播放卡顿; 视频播放太久,就无法及时响应用户的操作。并且查询按键和播放视频的代码混 杂在一起,代码丑陋。 如果使用多线程,线程 1 单独处理按键,线程 2 单独处理播放,可以完美解 决上述问题。
2 线程概念
所谓线程,就是操作系统所能调度的最小单位。普通的进程,只有一个线程在执行对应的逻辑。我们可以通过多线程编程,使一个进程可以去执行多个不同的任务。相比多进程编程而言,线程享有共享资源,即在进程中出现的全局变量, 每个线程都可以去访问它
3 如何理解(多)线程与(多)进程
(1)一个线程只属于一个进程,一个进程可包含多个线程。
(2)多线程是一个应用程序划分为若干给可并行运行的线程,每条线程独立运行。多线程就是同时运行多个程序实例
(3)线程享有共享资源,进程中的全局变量,每个线程都可去访问它。
(4)简单来说,一个多进程相当于有多个main()函数,每个main()函数分别执行一个功能。多线程只有一个main()函数,但是里面有多个独立函数,每个函数用于执行一个功能,该函数就是一个进程。
多进程图解:
多线程图解
4 多线程编程中部分用法
4.1 创建线程
#include <pthread.h> #包含头文件
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routi ne) (void *), void *arg); #创建新线程
(1)第一个参数为 pthread_t 指针,用来保存新建线程的线程号;
(2)第二个参数表示了线程的属性,一般传入 NULL 表示默认属性;
(3)第三个参数是一个函数指针,就是线程执行的函数。这个函数返回值为 void*, 形参为 void*。
(4)第四个参数则表示为向线程处理函数传入的参数,若不传入,可用 NULL 填充.
4.2互斥访问
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER; #初始化互斥
量也可以调用宏来快速初始化
pthread_mutex_lock(&g_tMutex); #上锁
pthread_mutex_unlock(&g_tMutex) #解锁
4.3同步操作(条件变量)
条件变量(条件变量可代替信号量,条件变量使用时,与互斥量一起)
static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER; #初始化同步量
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER; #初始化互斥
量也可以调用宏来快速初始化
//线程A:等待条件满足
pthread_mutex_lock(&g_tMutex);
pthread_cond_wait(&g_tConVar, &g_tMutex);
/* 操作临界资源 */
pthread_mutex_unlock(&g_tMutex);
//线程B:唤醒等待g_ConVar的线程
pthread_cond_signal(&g_ConVar)
4.4信号量(同步操作)
int sem_init(sem_t *sem,int pshared,unsigned int value); #使用信号量时,用该函数初始化
#include <pthread.h> #包含头文件
int sem_wait(sem_t *sem); #作用为检测指定信号量是否有资源可
用,若无资源可用会阻塞 等待
int sem_post(sem_t *sem); #会释放指定信号量的资源
注:当sem_post释放信号量后,sem_wait才能等到。
多线程编程实例
include <xxx.h>
...
static char g_but[100];
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER; #初始化互斥量
static pthread_cond_t g_ConVar = PTHREAD_COND_INITIALIZER; #初始化同步量
static void *My_thread_func(void *data)
{
while(1)
{
/*等待通知*/
pthread_mutex_lock(&g_tMutex); //获得互斥量
pthread_cond_wait(&g_tConVar,&g_tMutex); //判断体哦阿健是否成立,成立执行下面代码,
//不成立会去释放互斥量,继续等待,一旦当能返回,就表条件成功,
//并获得互斥量(等待唤醒)
/*打印*/
printf("recv:%s\n",g_buf);
pthread_mutex_unlock(&g_tMutex); //释放互斥量
}
return NULL;
}
int main(int argc,char ** argv)
{
pthread_t tid; //对于进程而言,每一个进程都有一个唯一对应的 PID 号来表示该
//进程,而对于线程而言,也有一个“类似于进程的 PID 号”,名为 tid
int ret;
char buf[1000];
/*1.创建“接收线程”*/
ret = pthread_create(&tid,NULL,My_thread_func,NULL);
if(ret) //判断是否创建成功
{
printf("pthread_create err!\n");
return -1;
}
/*2.主线程读取标准输入,发信息接收线程*/
while(1)
{
fgets(buf,1000,stdin); //---------|
pthread_mutex_lock(&g_tMutex); //上锁 // |---->防止互斥量卡在主线程
memcpy(g_buf,buf,1000) //---------|
pthread_cond_signal(&g_ConVar); //唤醒线程
pthread_mutex_unlock(&g_tMutex); //解锁
}
return 0;
}