线程操作:锁、条件变量的使用

简介: 线程操作:锁、条件变量的使用

关于本文提到的线程操作所用到的函数以及数据结构全部在一个头文件中定义:

#include<pthread.h>

1.线程的创建和销毁:

每一个线程都有一个id,叫做句柄,一个线程创建完毕时,它的句柄也被确定下来

句柄存放在句柄变量里,就像指针存放在指针变量里一样,句柄变量的类型是pthread_t

下面的代码创建了一个用于存放句柄的数组:

pthread_t threadid[100] = {0}

开始创建线程:

pthread_create(&thread[i], NULL, thread_callback, &argc);

第一个参数是对句柄变量的引用,需要pthread_create函数能对句柄变量进行修改,函数执行成功则将创建好的线程的句柄赋值给此句柄变量

第二个参数是创建线程的属性,一般写NULL,为默认

第三个参数是该线程执行的函数,需要函数的返回类型为void*,如果不是,需要强制转换

第四个参数是传入第三个参数的参数,需要void*类型,如果有多个参数,需要用一个结构体的引用来传入这些参数

主进程结束前需要确保子线程结束并正确销毁以释放资源,否则他将成为孤儿进程或僵尸进程,这两种情况都是应该避免的

可以在主进程中等待子进程正确完成:

pthread_join(thread[i], NULL); 

该函数会等待参数句柄所指代的线程完成,否则会阻塞

第一个参数是需要等待的句柄,不需要引用,第二个参数是用来获取该线程完成时的返回值,需要一个指针,线程的返回值会保存到这个指针,设置为NULL表示不需要获取该线程的返回值

2.互斥锁、自旋锁用来解决多个线程同时访问共享变量的问题,也叫做竞态条件,而出现竞态条件也算是时钟中断机制的一个缺点。

对共享变量操作的代码前后加锁解锁就可以避免竞态条件:

pthread_mutex_lock(&mutex);//加锁,参数是一个被初始化后的互斥锁的地址
 (*pcount)++; // 使用互斥锁对共享变量进行保护,避免竞态条件
 pthread_mutex_unlock(&mutex);//解锁,参数是一个互斥锁的地址

创建互斥锁变量:

pthread_mutex_t mutex;

其中pthread_mutex_t是互斥锁变量的类型

之所以为变量是因为它可以具有不同的属性:

初始化互斥锁变量:

pthread_mutex_inint(&mutex, NULL);

第一个参数是对一个互斥锁变量的引用

第二个参数规定了该互斥锁的属性,默认为NULL

互斥锁的生命周期:创建-> 初始化-> 被引用-> 被销毁

需要注意的是,使用未初始化的互斥锁变量可能会访问位置内存,造成崩溃,程序结束前未销毁互斥锁变量可能会造成资源泄露

互斥锁的销毁:

pthread_mutex_destory(&mutex);

对于自旋锁,操作上与互斥锁无区别,只需要把mutex字段改成spin

cas和原子操作另作文章

3.条件变量:

pthread_cond_t cond;

pthread_cond_t 是条件变量的类型, 用来声明一个条件变量,使用前需要初始化:

pthraed_cond_init(&cond, NULL)

第二个参数代表条件变量的属性,默认NULL

先看一个例子:

static void *thread_pool_callback(void *arg) {
    struct workers *worker = (struct workers*)arg;
    while (1) {
        pthread_mutex_lock(&worker->manager->mutex);
        while (worker->manager->tasks == NULL) {
            if (worker->terminate) break;
            pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex);
            if (worker->terminate) break;
        }
        if (worker->terminate) {
            pthread_mutex_unlock(&worker->manager->mutex);
            break;
        }
        struct tasks *task = worker->manager->tasks;
        LIST_DELETE(task, worker->manager->tasks);
        pthread_mutex_unlock(&worker->manager->mutex);
        task->task_func(task);
    }
    free(worker);
    return NULL;
}

程序会阻塞到pthread_cond_wait(&cond,&mutex)直到收到信号将它唤醒

而这个信号需要有函数来发送:

1.pthread_cond_signal(&cond):唤醒一个正在条件变量cond上等待的线程,如果有多个线程在等待,具体唤醒哪一个线程我们无从得知,取决于系统的调度策略

2.pthread_cond_broadcast(&cond):唤醒所有在cond上等待的线程

注意:使用信号来唤醒等待的线程时,wait和signal、broadcast必须使用同一个条件变量cond

相信你也注意到了,pthread_cond_wait(&cond, &mutex);的第二个参数是一个互斥锁变量,这是用来确保wait操作的原子性,也就是说pthread_cond_wait(&cond, &mutex);前后必须上锁、解锁

最后销毁条件变量:

pthread_cond_destory(&cond);

注意:在尝试销毁处于等待状态的条件变量时,会失败,不能过河拆桥!

目录
相关文章
|
2月前
|
安全 Java 编译器
线程安全问题和锁
本文详细介绍了线程的状态及其转换,包括新建、就绪、等待、超时等待、阻塞和终止状态,并通过示例说明了各状态的特点。接着,文章深入探讨了线程安全问题,分析了多线程环境下变量修改引发的数据异常,并通过使用 `synchronized` 关键字和 `volatile` 解决内存可见性问题。最后,文章讲解了锁的概念,包括同步代码块、同步方法以及 `Lock` 接口,并讨论了死锁现象及其产生的原因与解决方案。
81 10
线程安全问题和锁
|
2月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
45 2
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
1月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
35 1
|
1月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
37 0
|
1月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
27 0
|
1月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
32 0
|
1月前
|
安全 Java 程序员
【多线程-从零开始-肆】线程安全、加锁和死锁
【多线程-从零开始-肆】线程安全、加锁和死锁
44 0
|
1月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
2月前
|
存储 算法 Java
关于python3的一些理解(装饰器、垃圾回收、进程线程协程、全局解释器锁等)
该文章深入探讨了Python3中的多个重要概念,包括装饰器的工作原理、垃圾回收机制、进程与线程的区别及全局解释器锁(GIL)的影响等,并提供了详细的解释与示例代码。
29 0

热门文章

最新文章