进程线程(4)

线程的退出

(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;
}

        线程间的资源竞争

  •  共享资源:    临界资源
  • 临界区  :    访问共享资源(临界资源)那段代码 

        互斥锁:互斥     排他性   --- 要么不访问 要访问就是一次完整操作  (原子操作)

定义互斥锁 ==》初始化锁 ==》加锁 ==》解锁 ==》销毁

  1. 定义互斥锁:pthread_mutex_t   mutex; 
  2. 初始化锁:pthread_mutex_init();
  3. 加锁:pthread_mutex_lock(); 
  4. 解锁 :pthread_mutex_unlock(); 
  5. 销毁: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) 死锁产生的四个必要条件:

                四者缺一不可,均需满足

  1. 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
  2. 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
  3. 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
  4. 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。注:发生死锁一定有循环等待,但是循环等待不一定是死锁。
3.死锁的处理策略
  1. 预防死锁。破坏死锁产生的四个必要条件中的一个或几个,
  2. 避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
  3. 死锁的检测和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。
4.什么时候会发生死锁
  1. 对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源(CPU)的竞争是不会引起死锁的。
  2. 进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程P1P2分别申请并占有了资源 R1、R2,之后进程P1又紧接着申请资源R2,而进程P2又申请资源R1,两者会因为申请的资源被对方占有而阻塞,从而发生死锁。
  3. 信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的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");

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值