零基础小白学习Linux系统编程对线程的了解。对于引用了线程的程序的编译需要加 -lpthread
一、 进程与线程
进程:资源(CPU,内存等)分配的最小单位,是线程的容器;它有独立的地址空间,所以一般情况下不会影响其他进程。进程切换时,耗费资源较大,效率差。
线程:程序执行的最小单位,是进程的不同执行路径;它有自己的堆栈和局部变量,但没有单独的地址空间,所以一个线程死掉就等于整个进程死掉。
与多进程相比多线程更加“节俭”,线程间的通信更加方便。
二、线程常用API
对于线程:
pthread_create——线程创建
pthread_exit——线程退出
pthread_join——线程等待
对于互斥锁:
pthread_mutex_init——创建互斥锁
pthread_mutex_destroy——销毁互斥锁
pthread_mutex_lock——加锁
pthread_mutex_unlock——解锁
对于条件变量:
pthread_cond_init——创建条件变量
pthread_cond_destroy——销毁条件变量
pthread_cond_wait——等待
pthread_cond_signal——触发
三、 线程
1、创建线程
//包含头文件
#include <pthread.h>
//函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数含义
1、thread——其指向的内存单元被设置为新创建线程的ID。简单点说就是线程名称(自我理解)。
2、attr——线程属性,默认为NULL。
3、 (*start_routine) (void *)——函数指针,新创建的线程从该函数的地址开始运行,如果要传递的参数不止一个,则需要定义结构体,将结构体地址作为arg传入。
返回值:若成功返回0,否则返回错误编号
2、线程退出
//包含头文件
#include <pthread.h>
//函数
void pthread_exit(void *retval);
参数说明: retval是一个指针,可以通过线程等待函数pthread_join来访问该指针。
3、线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数说明:
1、 thread为需要等待的线程。
2、 retval 用以接收被等待线程传回的值,不需要接收则可设置为NULL。
4、线程ID的获取与比较
#include <pthread.h>
pthread_t pthread_self(void);
//返回值为调用线程的ID
int pthread_equal(pthread_t tid1, pthread_t tid2);
5、代码示例:
#include <stdio.h>
#include <pthread.h>
void* fun1(void* arg)
{
static int ret=10;
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:date is %d\n",*((int *)arg));
pthread_exit((void*)&ret);
}
int main()
{
int ret;
int date=50;
pthread_t t1;
int *pret;
ret=pthread_create(&t1,NULL,fun1,(void*)&date);
if(ret==0){
printf("main: create t1 success\n");
}
printf("main:ID=%ld\n",(unsigned long)pthread_self());
pthread_join(t1,(void **)&pret);
//对于此程序,若没有该等待,一般情况下t1线程还来不及执行主线程就退出了
printf("main:t1 quit,ret=%d\n",*pret);
return 0;
}
运行结果:
四、互斥锁
互斥量(mutex)是加锁与解锁所包含的内容,其本身就相当于一把锁。如果运行到某个线程时,为了让该线程的所有内容不间断执行完(可能会被其他没上锁的线程打断),我们就需要用到互斥锁。如果某个线程加锁后还没走到解锁步骤,任何其他想进行再次加锁的线程都会被阻塞。
1、互斥锁的创建与销毁
//头文件包含
#include <pthread.h>
//创建锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *restrict_attr);
//销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
参数含义:
mutex 锁的名称。
restrict_attr 锁的属性,默认为NULL。
2、加锁和解锁
//头文件包含
#include <pthread.h>
//加锁
int pthread_mutex_lock(pthread_mutex_t mutex);
//解锁
int pthread_mutex_unlock(pthread_mutex_t mutex);
3、代码示例:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex;
void* fun1(void* arg)
{
pthread_mutex_lock(&mutex);
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:date is %d\n",*((int *)arg));
pthread_mutex_unlock(&mutex);
}
void* fun2(void* arg)
{
int i;
pthread_mutex_lock(&mutex);
for(i=0;i<3;i++){
printf("t2:%ld thread is create\n",(unsigned long)pthread_self());
printf("t2:date is %d\n",*((int *)arg));
leep(1);
}
pthread_mutex_unlock(&mutex);
}
void* fun3(void* arg)
{
pthread_mutex_lock(&mutex);
printf("t3:%ld thread is create\n",(unsigned long)pthread_self());
printf("t3:date is %d\n",*((int *)arg));
pthread_mutex_unlock(&mutex);
}
int main()
{
int ret;
int date=50;
pthread_t t1;
pthread_t t2;
pthread_t t3;
pthread_mutex_init(&mutex,NULL);
ret=pthread_create(&t1,NULL,fun1,(void*)&date);
if(ret==0){
printf("main: create t1 success\n");
}
ret=pthread_create(&t2,NULL,fun2,(void*)&date);
if(ret==0){
printf("main: create t2 success\n");
}
ret=pthread_create(&t3,NULL,fun3,(void*)&date);
if(ret==0){
printf("main: create t3 success\n");
}
printf("main:ID=%ld\n",(unsigned long)pthread_self());
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果
五、条件变量
用于线程间相互控制。
1、创建与销毁条件变量
//头文件包含
#include <pthread.h>
//函数
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *restrict);
int pthread_cond_destroy(pthread_cond_t *cond);
参数说明:
cond——条件变量名。
restrict——属性,默认为NULL。
2.、等待与触发
//头文件包含
#include <pthread.h>
//等待:让线程阻塞
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//触发:触发正在等待的线程
int pthread_cond_signal(pthread_cond_t *cond);//向一个线程发信息
int pthread_cond_broadcast(pthread_cond_t *cond); //向多个线程发信息
参数说明:
cond——环境变量名
mutex——锁名
3、代码示例:满足线程t2的触发条件,线程t1才执行。
#include <stdio.h>
#include <pthread.h>
int p_date=0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void* fun1(void* arg)
{
static int cnt=0;
while(1){
//一进来处于阻塞状态
pthread_cond_wait(&cond,&mutex);
printf("t1 run--------------------------------\n");
printf("t1:%d\n",p_date);
p_date=0;
sleep(1);
if(cnt++==10){
exit(1);
}
}
}
void* fun2(void* arg)
{
while(1){
printf("t2:%d\n",p_date);
pthread_mutex_lock(&mutex);
p_date++;
if(p_date==3){
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
int ret;
int date=50;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
ret=pthread_create(&t1,NULL,fun1,(void*)&date);
ret=pthread_create(&t2,NULL,fun2,(void*)&date);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
运行结果:
以上内容重复十次。
补充:
对于互斥锁与条件变量的初始化,也可以用静态初始化即使用宏,如:
//头文件包含
#include <pthread.h>
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER
pthread_cond_t cond=PTHREAD_COND_INITIALIZER