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

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

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

#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);

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

目录
相关文章
|
4月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
51 2
|
19天前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
19天前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
2月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
45 6
|
3月前
|
运维 API 计算机视觉
深度解密协程锁、信号量以及线程锁的实现原理
深度解密协程锁、信号量以及线程锁的实现原理
56 2
|
3月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
63 0
|
3月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
36 0
|
3月前
|
Java API
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
【多线程】乐观/悲观锁、重量级/轻量级锁、挂起等待/自旋锁、公平/非公锁、可重入/不可重入锁、读写锁
50 0
|
3月前
|
安全 Java 程序员
【多线程-从零开始-肆】线程安全、加锁和死锁
【多线程-从零开始-肆】线程安全、加锁和死锁
67 0
|
3月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解