线程的退出
(1)pthread_exit()
(2)main函数中的return
(3)pthread_cancel
int pthread_cancel(pthread_t thread);
发送取消的请求
thread:要发送的线程的tid号
返回值:成功返回0;失败返回错误码。
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
void * do_something(void *arg)
{
static int a = 10;
while (1)
{
printf("------do_something ---- \n");
sleep(1);
}
return &a;
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid,NULL,do_something,NULL);
if (ret != 0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
sleep(3);
pthread_cancel(tid);
printf("----cancel thread----\n");
pthread_exit(NULL);
return 0;
}
任何一个线程调用exit,或主函数结束return都会造成进程结束。
线程资源回收
(1)pthread_detach();
可分离属性 ---子线程运行很久才结束 --- 设置分离属性,子程序执行任务较长,主线程也不需要关心子线程状态
int pthread_detach(pthread_t thread);
功能:分离线程
参数:thread 要分离的线程的tid
返回值:成功 0,失败 错误码
(2)pthread_join
int pthread_join(pthread_t thread, void **retval);
需要自己回收 ---线程的属性 (可结合性) --- 一般是子线程 在较短时间内运行完 .这种用于线程任务较短,主线程需要关心子线程状态
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
void * do_something(void *arg)
{
static int a = 10;
//线程的任务函数
printf("------do_something ---- \n");
//pthread_exit(NULL);
pthread_exit(&a);
//pthread_exit((void *)a);
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid,NULL,do_something,NULL);
if (ret != 0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
pthread_detach(tid);
void *retval;
if (pthread_join(tid,&retval) != 0)
{
perror("pthread_join fail");
return -1;
}
printf("---- main----%d\n",*(int *)retval);
//pthread_exit(NULL);
return 0;
}
锁
线程间的资源竞争
- 共享资源: 临界资源
- 临界区 : 访问共享资源(临界资源)那段代码
互斥锁:互斥 排他性 --- 要么不访问 要访问就是一次完整操作 (原子操作)
定义互斥锁 ==》初始化锁 ==》加锁 ==》解锁 ==》销毁
- 定义互斥锁:pthread_mutex_t mutex;
- 初始化锁:pthread_mutex_init();
- 加锁:pthread_mutex_lock();
- 解锁 :pthread_mutex_unlock();
- 销毁:pthread_mutex_destroy();
(1)初始化:pthread_mutex_init()
动态初始化: int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数:mutex //就是要初始化的 互斥锁变量
attr //属性 NULL 默认属性 --- 普通锁 ,读写锁
静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
(2)加锁、解锁、销毁
int pthread_mutex_lock(pthread_mutex_t *mutex); 加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex); 解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁
参数:mutex //要操作的那把锁
锁是一种线程间 同步机制
互斥锁 -- 保证线程对于共享资源的排他性访问 。 保证每个线程访问时的原子性操作
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
void* increment_counter(void* arg) {
for (int i = 0; i < 10000; ++i) {
// 锁定互斥锁,等待直到获得锁
pthread_mutex_lock(&mutex);
// 安全地更新计数器
counter++;
printf("Counter is now %d\n", counter);
// 释放互斥锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t t1, t2;
// 创建两个线程
pthread_create(&t1, NULL, increment_counter, NULL);
pthread_create(&t2, NULL, increment_counter, NULL);
// 等待线程结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Final counter value is %d\n", counter);
return 0;
}
(3)尝试获取锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
void* increment_counter_trylock(void* arg) {
for (int i = 0; i < 10000; ++i) {
// 尝试锁定互斥锁
if (pthread_mutex_trylock(&mutex) == 0) {
// 成功获取锁,更新计数器
counter++;
printf("Counter is now %d\n", counter);
// 释放互斥锁
pthread_mutex_unlock(&mutex);
} else {
// 未能获取锁,可以做一些其他的事情或者简单地重试
// 这里我们选择简单的重试
continue;
}
}
return NULL;
}
int main() {
pthread_t t1, t2;
// 创建两个线程
pthread_create(&t1, NULL, increment_counter_trylock, NULL);
pthread_create(&t2, NULL, increment_counter_trylock, NULL);
// 等待线程结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Final counter value is %d\n", counter);
return 0;
}
死锁
(1)概念
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前进,这种情况就是死锁。
很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。
(2) 死锁产生的四个必要条件:
四者缺一不可,均需满足
- 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
- 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
- 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。注:发生死锁一定有循环等待,但是循环等待不一定是死锁。
3.死锁的处理策略
- 预防死锁。破坏死锁产生的四个必要条件中的一个或几个,
- 避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
- 死锁的检测和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。
4.什么时候会发生死锁
- 对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源(CPU)的竞争是不会引起死锁的。
- 进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程P1P2分别申请并占有了资源 R1、R2,之后进程P1又紧接着申请资源R2,而进程P2又申请资源R1,两者会因为申请的资源被对方占有而阻塞,从而发生死锁。
- 信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的P操作在实现同步的P操作之前,就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)
总之,对不可剥夺资源的不合理分配,可能导致死锁。
附录
对于代码中的perror可使用下面的宏处理。
#include<errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
*********************************************************************
//此时
errno = ret;
perror("pthread_create fail");
等价于
perror("")handle_error_en(ret,"pthread_create fail");