pthread和semaphore的简单应用以及四个典型的多线程问题

pthread常用函数简单介绍

创建线程

int  pthread_create(pthread_t  *  thread,

pthread_attr_t * attr,

void * (*start_routine)(void *),

void * arg)

thread是一个pthread_t类型的指针,可以简单理解为线程ID

attr表示该线程的属性,具体没有看,下面的程序中都设置成了NULL,表示默认属性。

start_routine是线程函数体的函数指针

arg是线程函数的参数

线程函数的类型是 void *fun(void *)也就是可以带一个指针参数,也可以返回一个指针。

父线程回收子线程资源

当子线程运行结束后,还有以下资源要回收。

int pthread_join(pthread_t th, void **thread_return)

th使pthread_t类型的变量,可以理解为线程ID

thread_return 是子线程函数的返回值。

初始化一个互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex,

const pthread_mutex_attr_t *mutexattr);

mutex表示待初始化的互斥锁,mutexattr表示互斥锁的属性,没仔细研究,下面的程序中都是使用的NULL。表示默认属性

互斥锁枷锁和解锁

int pthread_mutex_lock(pthread_mutex *mutex);

int pthread_mutex_unlock(pthread_mutex *mutex);

销毁互斥锁

int pthread_mutex_destroy(pthread_mutex *mutex);

semaphore常用函数介绍

初始化信号量

int sem_init (sem_t *sem , int pshared, unsigned int value);

sem表示待初始化的信号量

pshared表示共享属性,Linux中貌似只能设置为0

value表示信号量的初始值

申请资源

int sem_wait(sem_t *sem);

释放资源

int sem_post(sem_t *sem);

销毁信号量

int sem_destroy(sem_t *sem);

一个常见的面试题

编写一个程序,开启3个线程,线程1输出A,线程2输出B,线程3输出C,要求输出结果必须按ABC的顺序显示;如:ABCABC….

典型的线程同步的问题:

线程1进行后线程2才能进行,然后才是线程3,线程3执行后线程1有开始执行。

也就是:

可以看到形成了一个环形,也就可能会因为出现环路等待而形成死锁,解决的办法就是,指定一个进程先执行,而且题目中让我们依次输出ABC,所以我们指定线程1先运行。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h> static sem_t A_B;
static sem_t B_C;
static sem_t C_A; void *printA(void *arg)
{
int i = ;
for(i = ;i < ;i++)
{
sem_wait(&C_A);
printf("第%02d次:A",i);
sem_post(&A_B); }
return NULL;
}
void *printB(void *arg)
{
int i = ;
for(i = ;i < ;i++)
{
sem_wait(&A_B);
printf("B");
sem_post(&B_C); }
return NULL;
}
void *printC(void *arg)
{
int i = ;
for(i = ;i < ;i++)
{
sem_wait(&B_C);
printf("C\n");
sem_post(&C_A); }
return NULL;
}
int main()
{
pthread_t thread_A;
pthread_t thread_B;
pthread_t thread_C;
sem_init(&A_B,,);
sem_init(&B_C,,);
sem_init(&C_A,,);
pthread_create(&thread_A,NULL,printA,NULL);
pthread_create(&thread_B,NULL,printB,NULL);
pthread_create(&thread_C,NULL,printC,NULL);
pthread_join(thread_A,NULL);
pthread_join(thread_B,NULL);
pthread_join(thread_C,NULL);
sem_destroy(&A_B);
sem_destroy(&B_C);
sem_destroy(&C_A);
printf("\n"); return ; }

生产者消费者问题

生产者消费者之间存在的互斥和同步关系分析

首先,同一时间,只允许一个生产者对当前该生产的位置进行访问,所以生产者与生产者之间是互斥关系。

再者,同一时间,只允许一个消费者对当前该消费的位置进行消费,所以消费者与消费者之间也是互斥关系

最后,同一个位置要先生产再消费,所以生产者和消费者之间是同步关系。

我在具体是现实,用了一个指针in表示下一个待生产的位置,由于生产者和消费者线程都需要访问这个指针in,所以in是一个临界区,生产者和消费者要互斥地访问,为了变成简便我直接也把生产者和消费者看成是互斥关系,但是这也导致临界区的粒度变大。

代码如下:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM 10
#define P_NUM 5
#define C_NUM 10 int buffer[];
int *in; pthread_mutex_t mutex;//buffer临界区 sem_t p_sem;//消费者信号量
sem_t c_sem;//生产者信号量 void *producer(void *arg)
{
while()
{
sem_wait(&p_sem); pthread_mutex_lock(&mutex);
printf("生产者生产了第%02d个位置的商品\n",*in);
in++;
pthread_mutex_unlock(&mutex); sem_post(&c_sem);
sleep();
}
return NULL;
}
void *consumer(void *arg)
{
while()
{
sem_wait(&c_sem); pthread_mutex_lock(&mutex);
in--;
printf("消费者消耗了第%02d个位置的商品\n",*in);
pthread_mutex_unlock(&mutex); sem_post(&p_sem);
sleep();
}
return NULL;
} int main()
{
int i = ;
for(i = ;i < NUM;i++)
{
buffer[i] = i + ;
} in = buffer; //初始化互斥体
pthread_mutex_init(&mutex,NULL); //初始化信号量
sem_init(&p_sem,,NUM);
sem_init(&c_sem,,); pthread_t ppt[P_NUM]; //创建生产者线程
for(i = ;i < P_NUM;i++)
{
pthread_create(&ppt[i],NULL,producer,NULL);
}
//创建消费者线程
pthread_t cpt[C_NUM];
for(i = ;i < C_NUM;i++)
{
pthread_create(&cpt[i],NULL,consumer,NULL);
} //回收资源
for(i = ;i < P_NUM;i++)
{
pthread_join(ppt[i],NULL);
}
for(i = ;i < C_NUM;i++)
{
pthread_join(cpt[i],NULL);
}
pthread_mutex_destroy(&mutex);
sem_destroy(&p_sem);
sem_destroy(&c_sem); return ;
}

读者写者问题

读者写者也是一个非常著名的同步问题。读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。

读者和写者之间存在互斥关系

写者和写者之间存在互斥关系

读者和读者之间没有互斥关系,是共享关系。

这个问题的解决分为读者优先和写者优先:

读者优先是这样的:一旦有读者成功访问,那么写者将被阻塞,允许后续读者,直到没有读者后,写者才被允许访问。

写者优先是这样的:一旦后写者申请访问,那么将阻止后续读者继续访问,等当前读者读完后,写者开始写。

读者优先代码如下:

/**
* 读者写者问题
* 读者优先
* 青儿哥哥
* 博客园
* 2017-09-27
* */ #include<stdio.h>
#include<stdlib.h>
#include<pthread.h> #define R_NUM 10 //读者个数
#define W_NUM 3 //写者个数 pthread_t rpt[R_NUM];//读者线程的id
pthread_t wpt[W_NUM];//写者线程的id int readercnt = ; int buffer = ; pthread_mutex_t buffer_mutex;//缓冲区的临界区 pthread_mutex_t readercnt_mutex;//读者数量的临界区 void write()
{
int rd = rand()%;
buffer = rd;
printf("写者写:%d\n",buffer);
}
void read()
{
printf("读者读:%d\n",buffer);
} void *reader(void *arg)
{
while()
{
pthread_mutex_lock(&readercnt_mutex);
readercnt++;
if(readercnt == )
{
pthread_mutex_lock(&buffer_mutex);
}
pthread_mutex_unlock(&readercnt_mutex); read(); pthread_mutex_lock(&readercnt_mutex);
readercnt--;
if(readercnt == )
{
pthread_mutex_unlock(&buffer_mutex);
}
pthread_mutex_unlock(&readercnt_mutex);
sleep(); }
return NULL; }
void *writer(void *arg)
{
while()
{
pthread_mutex_lock(&buffer_mutex);
write();
pthread_mutex_unlock(&buffer_mutex);
sleep();
}
return NULL;
} int main()
{
//初始化互斥体
pthread_mutex_init(&buffer_mutex,NULL);
pthread_mutex_init(&readercnt_mutex,NULL); int i = ;
for(i = ;i < R_NUM;i++)
{
pthread_create(&rpt[i],NULL,reader,NULL);
}
for(i = ;i < W_NUM;i++)
{
pthread_create(&wpt[i],NULL,writer,NULL);
} for(i = ;i < R_NUM;i++)
{
pthread_join(rpt[i],NULL);
}
for(i = ;i < W_NUM;i++)
{
pthread_join(wpt[i],NULL);
} pthread_mutex_destroy(&buffer_mutex);
pthread_mutex_destroy(&readercnt_mutex); return ;
}

写者优先代码如下:

/**
* 读者写者问题
* 写者优先
* 青儿哥哥
* 博客园
* 2017-09-27
* */ #include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h> #define R_NUM 10 //读者个数
#define W_NUM 3 //写者个数 pthread_t rpt[R_NUM];//读者线程的id
pthread_t wpt[W_NUM];//写者线程的id int readercnt = ;
int writercnt = ; int buffer = ; pthread_mutex_t buffer_mutex;//缓冲区的临界区 pthread_mutex_t readercnt_mutex;//读者数量的临界区
pthread_mutex_t writercnt_mutex;//写者数量的临界区 sem_t reader_sem;//读者的信号量 void write()
{
int rd = rand()%;
buffer = rd;
printf("写者写:%d\n",buffer);
}
void read()
{
printf("读者读:%d\n",buffer);
} void *reader(void *arg)
{
while()
{
sem_wait(&reader_sem);
sem_post(&reader_sem);
pthread_mutex_lock(&readercnt_mutex);
readercnt++;
if(readercnt == )
{
pthread_mutex_lock(&buffer_mutex);
}
pthread_mutex_unlock(&readercnt_mutex); read(); pthread_mutex_lock(&readercnt_mutex);
readercnt--;
if(readercnt == )
{
pthread_mutex_unlock(&buffer_mutex);
}
pthread_mutex_unlock(&readercnt_mutex);
sleep(); }
return NULL; }
void *writer(void *arg)
{
while()
{ pthread_mutex_lock(&writercnt_mutex);
writercnt++;
if(writercnt == )
{
sem_wait(&reader_sem);
}
pthread_mutex_unlock(&writercnt_mutex);
pthread_mutex_lock(&buffer_mutex);
write();
pthread_mutex_unlock(&buffer_mutex);
pthread_mutex_lock(&writercnt_mutex);
writercnt--;
if(writercnt == )
{
sem_post(&reader_sem);
}
pthread_mutex_unlock(&writercnt_mutex); sleep();
}
return NULL;
} int main()
{
//初始化互斥体
pthread_mutex_init(&buffer_mutex,NULL);
pthread_mutex_init(&readercnt_mutex,NULL);
pthread_mutex_init(&writercnt_mutex,NULL); sem_init(&reader_sem,,); int i = ;
for(i = ;i < R_NUM;i++)
{
pthread_create(&rpt[i],NULL,reader,NULL);
}
for(i = ;i < W_NUM;i++)
{
pthread_create(&wpt[i],NULL,writer,NULL);
} for(i = ;i < R_NUM;i++)
{
pthread_join(rpt[i],NULL);
}
pthread_mutex_destroy(&buffer_mutex);
pthread_mutex_destroy(&readercnt_mutex);
pthread_mutex_destroy(&writercnt_mutex);
sem_destroy(&reader_sem); return ; }

如果你觉得对你有用,请赞一个吧~~

pthread和semaphore的简单应用以及四个典型的多线程问题的更多相关文章

  1. posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序

    posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属 ...

  2. pthread库实现一个简单的任务池

    pthread库实现一个简单的任务池 类关系图: 说明:         1:TaskManager类管理Task类,Task类是一个纯虚类;         2:ThreadManager类管理Th ...

  3. SSD固态盘应用于Ceph集群的四种典型使用场景

    在虚拟化及云计算技术大规模应用于企业数据中心的科技潮流中,存储性能无疑是企业核心应用是否虚拟化.云化的关键指标之一.传统的做法是升级存储设备,但这没解决根本问题,性能和容量不能兼顾,并且解决不好设备利 ...

  4. 数据采集与融合第四次作业:多线程以及scrapy框架的使用

    数据采集第四次作业:多线程以及scrapy框架的使用 任务一:单多线程的使用 单线程代码: from bs4 import BeautifulSoup from bs4 import UnicodeD ...

  5. 线程模型、pthread 系列函数 和 简单多线程服务器端程序

    一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 “线程实现”建立在“进程控制”机制之上,由用 ...

  6. pthread创建线程的简单演示

      使用pthread创建子线程的简单步骤 导入头文件 #import <pthread.h> 指定新线程标识符 使用pthread创建线程的函数 根据result = 0 与否判断子线程 ...

  7. Vue 简单的总结四(项目流程,DIY脚手架、vue-cli的使用)

    项目流程 1.下载 cdn 2.引包 vue-router依赖vue vue-router.js 3.如果是模块化机制 Vue.use(vue-router) 4.创建示例 let Home = {/ ...

  8. FusionCharts简单教程(四)-----基本数字格式

          在统计图例中什么是最基本,最重要的元素?那就是数据.一个数据的统计图像那就是一堆空白.但是数据存在多种形式,比如小数,比如千分位等等.又如若一个数据是12.000000001,对于数据要求 ...

  9. Windowsclient开发简单介绍(四)

            在上一篇文章里,我简单扼要的给大家介绍了一下GDI的基础知识,包含DC,HDC.GDI对象等等,总的来说都是些偏理论的知识,属于概念的范畴. 今天这篇文章里,我就要正式開始有点实际的东 ...

随机推荐

  1. java web基础之mvc模式设计(一)--使用httpservlet实现mvc分层设计,DAO层使用的是dbutils实现与数据库的链接

    一:1.最终的实现效果图: 2.案例的目录结构: 3.案例中使用到的jar包: 二:案例的链接数据库的层次结构关系:数据库是:mysql ,数据库名字:dsm,表格名字:customers 数据库表格 ...

  2. [FRAMESET][PHP]Frameset下面使用php-header('location:...') redirect链接

    一般,我们的管理后台都是使用frameset来进行布局的,所以如果我们对后台的登录会话时间进行了设定,那么在超过该时间session失效之后,那么我们就必须要在php文件中进行判断处理. 判断会话失效 ...

  3. 取Mac地址

    uses Nb30; //一般用默认的 0 就可以了 ):string; var ncb : TNCB; {NetBios控制块} AdapterS : TAdapterStatus; {网卡状态结构 ...

  4. C99 中 main 函数的写法

    今天在论坛看见有人讨论 C 语言中 main 函数的写法,看到结论才知道 main 函数的正确写法. 被老谭酸菜坑了这么多年,还是记录下吧,或许以后某天不搞 .net,回去折腾 C 语言了. 写法1: ...

  5. [ACM_动态规划] hdu1003 Max Sum [最大连续子串和]

    Problem Description Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum ...

  6. 【C#进阶】委托那些事儿(一)

    一.简单的委托 1.1 委托的声明: C#当中,委托(delegate)是一种方法封装,也即委托对象可以作为一种传递方法的变量来使用. 委托也算是一种类,与类是平级的存在.在类中写delegate对象 ...

  7. C# 动态生成Html地图文件

    public void GPSModel(string x, string y, string ss)//动态地图文件 { if (x.Contains("-") &&am ...

  8. 虚幻4随笔6 Object和序列化

    诚如之前所说,虚幻4主要的一些特性都是由UObject穿针引线在一起的,想把虚幻玩到比较深的程度,UObject是迟早要面对.回避不得的问题,所以,准备在其它主题之前,先把UObject好好弄一下.U ...

  9. 四两拨千斤式的攻击!如何应对Memcache服务器漏洞所带来的DDoS攻击?

    本文由  网易云发布. 近日,媒体曝光Memcache服务器一个漏洞,犯罪分子可利用Memcache服务器通过非常少的计算资源发动超大规模的DDoS攻击.该漏洞是Memcache开发人员对UDP协议支 ...

  10. Restframework 视图组件与序列号组件的应用.

    models from django.db import models # Create your models here. class Course(models.Model): title=mod ...