目录
一,线程
1.概念
线程相当于一个轻量级的进程,可以提高系统的性能;在Linux系统中,同样使用结构体task_struct来描述一个线程;线程和进程都参加统一的调度。
在同一个进程中创建的线程共享该进程的地址空间。
2.进程和线程的区别
共同点:都为操作系统提供了
并发执行
的能力;
不同点:(1)线程是系统
调度
的最小单位;进程是
资源
分配的最小单位;
(2)同一个进程创建的多个线程
共享
进程的资源;进程的地址空间相互
独立
;
(3)线程通信相对简单,可通过
全局变量
实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制
(3G-4G的内核空间)
;
(4)线程安全性较差,当进程结束时会导致所有的线程退出,一个线程的结束可能会影响到其他线程;进程相对安全。
3.线程资源
共享的资源:可执行的指令,静态数据,进程中打开的文件描述符,信号处理函数,当前工作目录,用户ID,用户组ID。
私有的资源:线程ID(TID),PC(程序计数器)和相关寄存器,堆栈,错误码,信号掩码,优先级,执行状态和属性。
二,线程函数
编译包含线程的程序时,需要链接库“-lpthread”。
1.线程创建
函数:int
pthread_create
(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);
功能:创建线程;
参数:thread->线程标识;
attr->线程属性,常设置为默认属性NULL;
start_toutine->函数名,代表线程函数,函数的参数应为void *arg;
arg->用来给线程函数传参;
返回值:成功->0;失败->错误码;
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
//线程函数
void *handler(void *arg)
{
printf("in thread\n");
}
int main(int argc, char const *argv[])
{
//定义pthread_t类型的tid
pthread_t tid;
//判断,返回值不为0则创建失败
if(pthread_create(&tid,NULL,handler,NULL) != 0)
{
perror("thread create err");
return -1;
}
printf("in main\n");
//不一定会进入线程,所以使用循环保证线程运行
while(1);
return 0;
}
2.线程退出与线程回收
(1) 函数:int
pthread_exit
(void *value_ptr);
功能:执行线程退出的功能;
参数:value_ptr->线程退出时返回的值,一般设置为NULL;
返回值:成功->0;失败->错误码;
(2) 函数:int
pthread_join
(pthread_t thread,void **value_ptr);
功能:用于等待一个指定的线程结束;
参数:thread->创建的线程对象;
value_ptr->指针*value_ptr指向线程返回的参数,一般设置为NULL;
返回值:成功->0;失败->错误码。
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *handler(void *arg)
{
printf("in thread\n");
//pthread_exit(NULL);//结束线程
pthread_exit("hello");
}
int main(int argc, char const *argv[])
{
pthread_t tid;
if(pthread_create(&tid,NULL,handler,NULL) != 0)
{
perror("thread create err");
return -1;
}
printf("in main\n");
//接收线程返回的参数hello
void *ch;
pthread_join(tid,&ch);
printf("%s\n",(char *)ch);
//pthread_join(th,NULL);//回收线程资源
return 0;
}
3.线程同步(信号量)
(1)概念:
同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情;
(2)信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待;
信号量代表某一类资源,其值表示系统中该资源的数量;
信号量是一个受保护的变量,只能通过三种操作来访问:初始化、P操作(申请资源)、V操作(释放资源);
信号量的值为非负整数。
(3)函数接口
①int
sem_init
(sem_t *sem, int pshared, unsigned int value)
功能:初始化信号量;
参数:sem->初始化的信号量对象;
pshared->信号量共享的范围
(0: 线程间使用 非0:1进程间使用);
value->信号量初值;
返回值:成功为0,失败为-1;
sem_t sem;
if(sem_init(&sem,0,0) < 0)
{
perror("sem init err");
return -1;
}
②int
sem_wait
(sem_t *sem)
功能:申请资源,
P
操作;
参数:sem->信号量对象;
返回值:成功为0,失败为-1;
注:此函数执行过程,执行到wait,先对信号量进行减1,若减完的信号量的值大于等于0,表示有资源可以用,则继续执行;当信号量的值小于0时,表示没有资源可以使用,函数阻塞。
③int
sem_post
(sem_t *sem)
功能:释放资源,
V
操作;
参数:sem->信号量对象;
返回值:成功为0,失败为-1;
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
char ch[32] = "";
sem_t sem;
void *handler(void *arg)
{
while(1)
{
//给信号量 -1
sem_wait(&sem);
if((strcmp(ch,"quit") == 0))
{
pthread_exit(NULL);
}
printf("thread\n");
printf("output:%s\n",(char *)arg);
}
}
int main(int argc, char const *argv[])
{
//创建线程
pthread_t tid;
//给线程函数传递参数ch,先强转为void *类型
if(pthread_create(&tid,NULL,handler,(void *)&ch) != 0)
{
perror("thread create err");
return -1;
}
//初始化信号量(信号量对象,0->线程间使用,信号量初值)
if(sem_init(&sem,0,0) < 0)
{
perror("sem init err");
return -1;
}
while(1)
{
scanf("%s",ch);
//信号量+1
sem_post(&sem);
if(strcmp(ch,"quit")==0)
break;
}
pthread_join(tid,NULL);
printf("main\n");
return 0;
}
执行结果:输入一串字符串后,信号量先增加1,如果字符串不是“quit”,则线程中的wait判断信号量不为0,字符串也不为“quit”,就输出字符串,然后如果线程继续循环,wait检测到信号量为0时,将会继续运行主进程的循环,继续输入字符串,信号量再+1。
4.线程互斥与线程条件变量
1.线程互斥
1.相关概念
(1)临界资源:一次仅允许一个进程所使用的资源;
(2)临界区:指的是一个访问共享资源的程序片段;
(3)互斥:多个线程在访问临界资源时,同一时间只能被一个线程访问;
(4)互斥锁:通过互斥锁可以实现互斥机制,使用互斥锁的目的是用来保护临界资源,每个临界资源都应该由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。
2.函数接口
(1)int
pthread_mutex_init
(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
功能:初始化互斥锁;
参数:mutex ->互斥锁,用pthread_mutex_t来定义该参数;
pthread_mutex_t lock;
attr ->互斥锁属性 ,一般设置为NULL;
返回值:成功为0,失败为-1;
(2)int
pthread_mutex_lock
(pthread_mutex_t *mutex)
功能:
申请
互斥锁;
参数:mutex->互斥锁;
//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
{
perror("mutex init err");
return -1;
}
//上锁
pthread_mutex_lock(&lock);
返回值:成功为0,失败为-1;
注:pthread_mutex_lock和pthread_mutex_trylock区别:pthread_mutex_lock申请不到锁是会阻塞函数的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回。
(3)int
pthread_mutex_unlock
(pthread_mutex_t *mutex)
功能:
释放
互斥锁;
参数:mutex->互斥锁;
//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
{
perror("mutex init err");
return -1;
}
//上锁
pthread_mutex_lock(&lock);
//解锁
pthread_mutex_unlock(&lock);
返回值:成功为0,失败为-1。
(4)int
pthread_mutex_destroy
(pthread_mutex_t *mutex)
功能:销毁互斥锁;
参数:mutex->互斥锁;
代码演示:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
//互斥锁参数
pthread_mutex_t lock;
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int i;
//线程函数
void *handler_swap(void *arg)
{
int temp;
while(1)
{
//上锁
pthread_mutex_lock(&lock);
for(i = 0;i < 5;i++)
{
temp = a[i];
a[i] = a[9-i];
a[9-i] = temp;
}
//解锁
pthread_mutex_unlock(&lock);
}
}
//线程函数
void *handler_printf(void *arg)
{
while(1)
{
//上锁
pthread_mutex_lock(&lock);
for(i = 0;i < 9;i++)
{
printf("%d ",a[i]);
}
putchar(10);
//解锁
pthread_mutex_unlock(&lock);
sleep(1);
}
}
int main(int argc, const char *argv[])
{
//创建两个线程
pthread_t tid1,tid2;
if(pthread_create(&tid1,NULL,handler_swap,NULL)!=0)
{
perror("thread create err");
return -1;
}
if(pthread_create(&tid2,NULL,handler_printf,NULL)!=0)
{
perror("thread create err");
return -1;
}
//初始化锁
if(pthread_mutex_init(&lock,NULL)<0)
{
perror("mutex init err");
return -1;
}
//回收线程资源
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
执行结果为:在终端中循环正序输出0到9,或9到0,不能确定输出的次数,但是不会打乱一次输出中的顺序。
2.线程条件变量
条件变量
需要与
互斥锁
搭配使用,实现
同步机制
;
(1)int
pthread_cond_init
(pthread_cond_t *restrict cond,const pthread_condattr_t
*restrict attr);
功能:
初始化
条件变量;
参数:cond->是一个指向结构
pthread_cond_t
的指针;
pthread_cond_t cond;
pthread_cond_init(&cond,NULL);
restrict attr->是一个指向结构pthread_condattr_t的指针,
一般设为
NULL;
返回值:成功为0,
失败为非0。
初始化时,通过判断返回值是否小于0,来确定是否初始化成功,小于0则初始化失败
(2)int
pthread_cond_wait
(pthread_cond_t
*restrict cond,pthread_mutex_t
*restrict mutex);
功能:
等待信号
的产生;
参数:restrict cond->要等待的条件;
restrict mutex->对应的锁;
返回值:成功为0
,失败不为0。
注:当没有条件产生时->函数阻塞,锁解开;
等待到条件产生->函数结束阻塞,同时上锁。
pthread_cond_wait(&cond,&lock);
(3)int
pthread_cond_signal
(pthread_cond_t
*cond);
功能:给条件变量发送信号,
产生信号;
参数:cond->条件变量值;
pthread_cond_signal(&cond);
返回值:成功为0,失败为非0;
注:必须等待pthread_cond_wait函数先执行,再产生条件信号。
此函数和pthread_cond_broadcast区别是:
pthread_cond_broadcast函数相当于是广播,会将所有等待此条件的线程唤;
pthread_cond_signal只能唤醒单个等待此条件的线程。
(4)int
pthread_cond_destroy
(pthread_cond_t
*cond);
功能:将条件变量销毁;
参数:cond->条件变量值;
返回值:成功为
0
,失败为非0;
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//函数pthread_mutex_init()中的参数
pthread_mutex_t lock;
//函数pthread_cond_init()中的参数
pthread_cond_t cond;
int money = 0;
void *handler_get(void *arg)
{
while(1)
{
pthread_mutex_lock(&lock);//上锁
if(money < 100)
{
//当没有条件cond产生时阻塞,同时将锁打开;
//等待到条件产生时,函数立即返回同时进行上锁;
pthread_cond_wait(&cond,&lock);
}
if(money >= 100)
{
money -= 100;
printf("money:%d\n",money);
}
pthread_mutex_unlock(&lock);//解锁
}
}
void *handler_save(void *arg)
{
while(1)
{
sleep(1);//睡眠一秒确保get线程先执行
pthread_mutex_lock(&lock);//上锁
scanf("%d",&money);
pthread_mutex_unlock(&lock);//解锁
pthread_cond_signal(&cond);//产生cond条件(信号);
}
}
int main(int argc, char const *argv[])
{
pthread_t tid1,tid2;
//创建线程:tid->pthread_t,NULL,线程函数名,函数参数
if(pthread_create(&tid1,NULL,handler_get,NULL)!=0)
{
perror("create tid1 err");
return -1;
}
if(pthread_create(&tid2,NULL,handler_save,NULL)!=0)
{
perror("create tid2 err");
return -1;
}
//初始化锁
//参数:取地址-定义的pthread_mutex_t lock,状态NULL
//返回值小于0时,创建失败
if(pthread_mutex_init(&lock,NULL) < 0)
{
perror("mutex init err");
return -1;
}
//初始化条件变量
//参数:取地址-定义的pthread_cond_t cond,状态NULL
//返回值小于0时,创建失败
if(pthread_cond_init(&cond,NULL) < 0)
{
perror("cond init err");
return -1;
}
//回收线程资源
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
执行结果: