一.线程概述
1.1什么是线程,什么是进程,它们间的联系和区别?
- 进程:进程是程序运行的实例,而程序只是指令,数据以及组织形式的描述。同时进程是分配资源的最小单位。
- 线程:线程是程序执行的最小单位。
- 联系:线程被包含在进程中。一个进程至少有一个线程,表示进程同时只做一件事。线程和进程所有的数据和信息都是共享的,共用一个地址空间。
- 区别:进程拥有自己独立的地址空间,奔溃不会对其他进程产生影响。线程有自己的栈堆和局部变量,但依赖于所属进程的地址空间,线程死掉,整个进程都会死掉。所以多进程会比多进程健壮,但耗费资源较大且效率要差一些。
1.2为什么用线程?
- 相对进程线程耗费资源少,比较节俭
- 相对进程线程之间共享数据空间,所以具有方便的通信机制
- 提高应用程序响应速度
- 在线程数量不多于cpu数量时,多cpu系统更加有效,不同线程运行在不同cpu上
- 改善程序结构,利于程序的理解和修改
二.线程开发API(线程,互斥锁,条件:3+4+5)
2.1线程的创建,退出及等待
- 创建:pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
第一个参数pthread:一个整型指针,指向新建线程的ID
第二个参数attr:线程的属性,设为NULL,创建默认属性线程
第三个参数start_toutine():执行线程任务的函数,线程从函数的地址开始执行,
第四个参数:指针,指向存放函数参数的结构
- 退出:pthread_exit()
#include <pthread.h>
void pthread_exit(void *retval);
- 等待:pthread_join()
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- 实例:
#include<stdio.h>
#include <pthread.h>
void *func(void *arg){
static char *retval = "this is retval of pthread_join";
printf("t1:pthread_self = %ld\n ",(unsigned long)pthread_self());
printf("t1:parama = %s\n",(char *)arg);
pthread_exit((void *)retval);
}
int main(){
int ret;
char *parama ="this is a test" ;
char *retval = NULL;
pthread_t t1;
ret = pthread_create(&t1,NULL,func,(void*)parama);
if(ret == 0){
printf("main:create pthread success\n");
printf("main:pthread_self = %ld\n ",(unsigned long)pthread_self());
}
pthread_join(t1,(void **)&retval);
printf("retval = %s\n",retval);
return 0;
}
2.2互斥锁
- 互斥锁的意义在于,同一时间只允许一个线程运行和访问共享资源,防止出现数据不一致的问题。
- 互斥锁相关API:
- 创建及销毁互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//成功返回0,失败返回错误编码
- 加锁,解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- 例子:
#include<stdio.h>
#include <pthread.h>
#include<unistd.h>
#include <stdlib.h>
pthread_mutex_t mutex;
void *func1(void *arg){
pthread_mutex_lock(&mutex);
printf("t1\n");
printf("t1\n");
pthread_mutex_unlock(&mutex);
}
void *func2(void *arg){
pthread_mutex_lock(&mutex);
printf("t2t2\n");
printf("t2t2\n");
pthread_mutex_unlock(&mutex);
}
void *func3(void *arg){
pthread_mutex_lock(&mutex);
printf("t3t3t3\n");
printf("t3t3t3\n");
pthread_mutex_unlock(&mutex);
}
int main(){
int ret1;
int ret2;
int ret3;
pthread_t t1;
pthread_t t2;
pthread_t t3;
int parama =100;
pthread_mutex_init(&mutex,NULL);
ret1 = pthread_create(&t1,NULL,func1,(void*)¶ma);
if(ret1 != 0){
printf("creat t1 fail\n");
exit(-1);
}
ret2 = pthread_create(&t2,NULL,func2,(void*)¶ma);
if(ret2 != 0){
printf("creat t2 fail\n");
exit(-1);
}
ret3 = pthread_create(&t3,NULL,func3,(void*)¶ma);
if(ret3 != 0){
printf("creat t3 fail\n");
exit(-1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_join(t3,NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
2.3 死锁
- 当前线程的锁(资源)被其它线程占用且得不到释放,导致当前线程被一直锁住。
- 例如,t1把t2需要的锁先拿到了,t2则先把t1需要的锁拿到了,导致t1,t2相互锁死:
#include<stdio.h>
#include <pthread.h>
#include<unistd.h>
#include <stdlib.h>
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
void *func1(void *arg){
pthread_mutex_lock(&mutex2);
sleep(1);
pthread_mutex_lock(&mutex1);
printf("t1\n");
printf("t1\n");
pthread_mutex_unlock(&mutex1);
}
void *func2(void *arg){
pthread_mutex_lock(&mutex1);
sleep(1);
pthread_mutex_lock(&mutex2);
printf("t2t2\n");
printf("t2t2\n");
pthread_mutex_unlock(&mutex2);
}
int main(){
int ret1;
int ret2;
pthread_t t1;
pthread_t t2;
int parama =100;
pthread_mutex_init(&mutex1,NULL);
pthread_mutex_init(&mutex2,NULL);
ret1 = pthread_create(&t1,NULL,func1,(void*)¶ma);
if(ret1 != 0){
printf("creat t1 fail\n");
exit(-1);
}
ret2 = pthread_create(&t2,NULL,func2,(void*)¶ma);
if(ret2 != 0){
printf("creat t2 fail\n");
exit(-1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
2.4条件锁
-
互斥锁规定同一时间,最多允许多少个线程运行;而条件锁则保证同一时间这些线程运行的顺序,条件锁可以和互斥锁配合使用。
-
创建和销毁
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- 触发和广播
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
- 等待
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
- 实战(验证线程和进程共享内存空间)
#include<stdio.h>
#include <pthread.h>
#include<unistd.h>
#include <stdlib.h>
pthread_cond_t cond1;
pthread_cond_t cond2;
pthread_mutex_t mutex;
int data = 1;
int cnt = 1;
void *func1(void *arg){
pthread_mutex_lock(&mutex);
while(1){
if(cnt <= 3){
printf("t1:data = %d \n",data);
data++;
sleep(1);
cnt++;
}else{
cnt = 1;//计数重置
pthread_cond_signal(&cond1);//发送信号给,解锁t2
pthread_cond_wait(&cond2,&mutex);//等待t2
}
}
pthread_mutex_unlock(&mutex);
}
void *func2(void *arg){
pthread_mutex_lock(&mutex);
while(1){
if(data == 1){
pthread_cond_wait(&cond1,&mutex);//第一次,让t2等t1,保证t1先运行
}
if(cnt <= 3){//每个线程打印三次
printf("t2:data = %d \n",data);
data++;
sleep(1);
cnt++;
}else {
cnt = 1;//达到三次,计数重置
pthread_cond_signal(&cond2);//发送信号,解锁t1
pthread_cond_wait(&cond1,&mutex);//等待t1
}
}
pthread_mutex_unlock(&mutex);
}
int main(){
int ret1;
int ret2;
pthread_t t1;
pthread_t t2;
int parama =100;
pthread_cond_init(&cond1,NULL);
pthread_cond_init(&cond2,NULL);
pthread_mutex_init(&mutex,NULL);
ret1 = pthread_create(&t1,NULL,func1,(void*)¶ma);
if(ret1 != 0){
printf("creat t1 fail\n");
exit(-1);
}
ret2 = pthread_create(&t2,NULL,func2,(void*)¶ma);
if(ret2 != 0){
printf("creat t2 fail\n");
exit(-1);
}
pthread_join(t1,NULL);
pthread_join(t1,NULL);
pthread_cond_destroy(&cond1);
pthread_cond_destroy(&cond2);
pthread_mutex_destroy(&mutex);
return 0;
}
ztj@ubuntu:~/part2/PTHREAD$ ./a.out
t1:data = 1
t1:data = 2
t1:data = 3
t2:data = 4
t2:data = 5
t2:data = 6
t1:data = 7
t1:data = 8
t1:data = 9
t2:data = 10
t2:data = 11
t2:data = 12
t1:data = 13
t1:data = 14
t1:data = 15
t2:data = 16
t2:data = 17
t2:data = 18
t1:data = 19
t1:data = 20
t1:data = 21
t2:data = 22
t2:data = 23
t2:data = 24
t1:data = 25
^C
ztj@ubuntu:~/part2/PTHREAD$