多线程和线程池(系统编程五)

18 篇文章 0 订阅
16 篇文章 0 订阅

一、线程的同步与互斥

1. 互斥锁

(一)引入

多个进程都要去访问同一个资源(共享资源),需要去协调

多个线程去访问同一个资源也存在这个问题

死锁: 两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

产生死锁两种常见原因:

①线程上锁以后忘记解锁了,导致自己无法再次上锁,其他线程也不能上锁

②线程上锁后没来得及解锁,线程就被取消了

特点:

①多个线程使用同一把锁,一个线程上锁了,那么其他线程上锁的时候就上不了,会被阻塞

②上锁操作跟解锁操作配合使用的,如果只有上锁,没有解锁,就会死锁

(二)相关的接口函数

(1)初始化互斥锁
#include <pthread.h>

int  pthread_mutex_init(pthread_mutex_t  *mutex,  const  pthread_mutexattr_t *mutexattr);
参数:mutex --- 互斥锁变量
     mutexattr --- 互斥锁的属性,默认设置为NULL,使用系统默认的属性
(2)锁的基本操作(重点)
#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);   //上锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);  //解锁
参数:mutex --- 互斥锁变量
(3)锁的销毁
#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:mutex --- 互斥锁变量

2.条件变量

(一)引入

在开发过程中必须配合互斥锁一起来使用

条件变量是指互斥锁在上锁执行代码的时候,发生某个条件的时候,条件变量可以阻塞当前线程,或者条件变量也可以解除某个线程的阻塞

注意:所谓的发生某个条件,使用的时候根据实际情况去写条件

(二)相关的接口函数

(1)初始化条件变量
#include <pthread.h>

int    pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
参数:cond --- 条件变量
     cond_attr --- 条件变量的属性,一般设置为NULL,表示默认属性
(2)条件变量的使用

阻塞条件变量

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);    
参数:cond --- 条件变量
     mutex --- 互斥锁变量

特点: 先解锁然后阻塞当前线程(pthread_cond_wait函数有两个功能)

唤醒条件变量(解除阻塞)

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);    //唤醒其中一个线程
int pthread_cond_broadcast(pthread_cond_t *cond);  //唤醒所有的线程
参数:cond --- 条件变量

特点: 上锁然后解除当前线程的阻塞

(3)条件变量的销毁
#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
参数:cond --- 条件变量

3.线程中的信号量

原理:跟进程间通信的信号量一模一样

总结:linux中有三种信号量

SYSTEM-V IPC信号量        进程间
POSIX无名信号量            线程间
POSIX有名信号量            进程间

(一)POSIX无名信号量

(1)创建无名信号量
#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);  //semget()和semctl(  SETVAL)
参数: sem --- 信号量变量
      pshared --- 0  表示这个信号在线程间使用
                  非零 表示在进程间使用
      value --- 信号量的值
(2)PV操作
#include <semaphore.h>

int sem_wait(sem_t *sem);  //p操作,默认只能减1
int sem_post(sem_t *sem);  //v操作,默认只能加1
参数: sem --- 信号量变量
(3)销毁无名信号量
#include <semaphore.h>

int sem_destroy(sem_t *sem);
参数: sem --- 信号量变量

(二)POSIX有名信号量

(1)创建有名信号量
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);  //打开有名信号
sem_t *sem_open(const char *name, int oflag,  mode_t mode, unsigned int value);  //新建有名信号量
返回值:成功返回新建好的有名信号量

参数:name --- 你想打开或者新建的那个有名信号量的名字
     oflag --- O_CREAT and O_EXCL  
     mode --- 权限 0777
     value --- 有名信号量的值
(2)PV操作
#include <semaphore.h>

int sem_wait(sem_t *sem);  //p操作,默认只能减1
int sem_post(sem_t *sem);  //v操作,默认只能加1 
参数: sem --- 信号量变量
(3)关闭有名信号量
#include <semaphore.h>

int sem_close(sem_t *sem);  //关闭
int sem_unlink(const char *name); //删除有名信号量
参数: sem --- 信号量变量
      name --- 你想删除的那个有名信号量的名字

注意:

第一个:只要你新建了有名信号量,默认在ubuntu的/dev/shm/里面会生成一个有名信号量文件,文件名字 sem.xxxx(xxxx是你代码取的名字)

二、配合解决死锁的第二种原因

1. 第一种原因:

解决方法: 自己把解锁的代码加上即可

2.第二种原因:

解决方法:

(一)方法一

把线程设置为不可以取消

(二)方法二

调用linux中提供

#include <semaphore.h>

void pthread_cleanup_push(void (*routine)(void *),  void *arg);  //当线程被取消的时候,这个函数会被自动调用
void pthread_cleanup_pop(int execute);
参数:void (*routine)(void *) --- 函数指针
     arg --- 传递给routine的参数
     execute --- 0 表示不执行routine指向的函数(线程被取消才执行)
                 非零 表示执行routine指向的函数(线程被不被取消都执行)

注意:push和pop两个函数必须成对出现,只用一个会编译出错

三、线程池

1.概念

由多个线程组成的集合称为线程池

线程池创建的目的是为了并发执行任务

2.相关设计

(一)相关结构体

struct task
{
	void *(*task)(void *arg);
	void *arg;
	struct task *next;
};

(二)相关接口函数

(1)线程池初始化
#include <thread_pool.h>

bool init_pool(thread_pool *pool,unsigned int threads_number);
返回值:成功返回true,失败返回false

参数:pool --- 线程池指针
    threads_number --- 初始活跃线程个数(大于等于1)
(2)投送任务
#include <thread_pool.h>

bool add_task(thread_pool *pool,void *(*do_task)(void *arg),void *arg);
返回值:成功返回true,失败返回false

参数:pool --- 线程池指针
     do_task --- 投送至线程池的执行例程
     arg --- 执行例程 do_task的参数,不需要则为NULL
(3)增加活跃线程
#include <thread_pool.h>

int add_thread(thread_pool *pool,unsigned int additional_threads);
返回值:>0 --- 实际新增线程个数
       -1 --- 失败
              
参数:pool --- 需要增加线程的线程池指针
     additional_threads --- 新增线程个数
(4)删除活跃线程
#include <thread_pool.h>

int remove_thread(thread_pool *pool,unsigned int removing_threads);
返回值:>0 ---当前线程池剩余线程个数
       -1 --- 失败
              
参数:pool --- 需要删除的线程的线程池指针
     removing_threads --- 要删除的线程的个数,设置为0时直接返回当前线程池线程总数,对线程池不造成任何其他影响
(5)销毁线程池
#include <thread_pool.h>

bool destroy_pool(thread_pool *pool)
返回值:成功返回true,失败返回false

参数:pool --- 要销毁的线程池
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java.L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值