pthread_mutex_init()函数详解-程序员宅基地

技术标签: C++  linux  多线程  

linux下为了多线程同步,通常用到锁的概念。
posix下抽象了一个锁类型的结构:ptread_mutex_t。通过对该结构的操作,来判断资源是否可以访问。顾名思义,加锁(lock)后,别人就无法打开,只有当锁没有关闭(unlock)的时候才能访问资源。
即对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
使用互斥锁(互斥)可以使线程按顺序执行。通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程。互斥锁还可以保护单线程代码。
要更改缺省的互斥锁属性,可以对属性对象进行声明和初始化。通常,互斥锁属性会设置在应用程序开头的某个位置,以便可以快速查找和轻松修改。

l 头文件:
#include <pthread.h>

l 函数原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

l 函数作用:
该函数用于C函数的多线程编程中,互斥锁的初始化。

pthread_mutex_init() 函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空(NULL),则使用默认的互斥锁属性,默认属性为快速互斥锁 。互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。
pthread_mutexattr_init() 函数成功完成之后会返回零,其他任何返回值都表示出现了错误。

函数成功执行后,互斥锁被初始化为未锁住态。

l 互斥锁pthread_mutex_t的使用:

1. 互斥锁创建和销毁

有两种方法创建互斥锁,静态方式动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy()用于注销一个互斥锁,API定义如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex)

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

2.互斥锁属性

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争

3.其他锁操作

  锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

  int pthread_mutex_lock(pthread_mutex_t *mutex)
  int pthread_mutex_unlock(pthread_mutex_t *mutex)
  int pthread_mutex_trylock(pthread_mutex_t *mutex)

  pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

4.死锁

  死锁主要发生在有多个依赖锁存在时, 会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生. 如何避免死锁是使用互斥量应该格外注意的东西。
  总体来讲, 有几个不成文的基本原则:
  对共享资源操作前一定要获得锁。
  完成操作以后一定要释放锁。
  尽量短时间地占用锁。
  如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
  线程错误返回时应该释放它所获得的锁。

下面是一段测试代码,创建两个线程,分别访问全局变量gnum,并且修改它,打印出来.

/* mutex.c */
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>

/* 全局变量 */
int gnum = 0;
/* 互斥量 */
pthread_mutex_t mutex;

/* 声明线程运行服务程序. */
static void pthread_func_1(void);
static void pthread_func_2(void);

int main (void)
{
 /*线程的标识符*/
  pthread_t pt_1 = 0;
  pthread_t pt_2 = 0;
  int ret = 0;

  /* 互斥初始化. */
  pthread_mutex_init(&mutex, NULL);
  /*分别创建线程1、2*/
  ret = pthread_create(&pt_1,  //线程标识符指针
                       NULL,  //默认属性
                       (void*)pthread_func_1, //运行函数
                       NULL); //无参数
  if (ret != 0)
  {
     perror ("pthread_1_create");
  }

  ret = pthread_create(&pt_2, //线程标识符指针
                       NULL,  //默认属性
                       (void *)pthread_func_2, //运行函数
                       NULL); //无参数
  if (ret != 0)
  {
     perror ("pthread_2_create");
  }
  /*等待线程1、2的结束*/
  pthread_join(pt_1, NULL);
  pthread_join(pt_2, NULL);

  printf ("main programme exit!/n");
  return 0;
}

/*线程1的服务程序*/
static void pthread_func_1(void)
{
  int i = 0;

  for (i=0; i<3; i++) {
    printf ("This is pthread_1!/n");
    pthread_mutex_lock(&mutex); /* 获取互斥锁 */
    /* 注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调用. */
    sleep (1);
    /*临界资源*/
    gnum++;
    printf ("Thread_1 add one to num:%d/n", gnum);
    pthread_mutex_unlock(&mutex); /* 释放互斥锁. */
  }

  pthread_exit(NULL);
}

/*线程2的服务程序*/
static void pthread_func_2(void)
{
  int i = 0;

  for (i=0; i<5; i++)  {
    printf ("This is pthread_2!/n");
    pthread_mutex_lock(&mutex); /* 获取互斥锁. */
    /* 注意,这里以防线程的抢占,以造成一个线程在另一个线程sleep时多次访问互斥资源,所以sleep要在得到互斥锁后调用. */
    sleep(1);
    /* 临界资源. */
    gnum++;
    printf ("Thread_2 add one to num:%d/n",gnum);
    pthread_mutex_unlock(&mutex); /* 释放互斥锁. */
  }

  pthread_exit (NULL);
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Primeprime/article/details/105617856

智能推荐

ESP32 开发笔记(三)源码示例 21_WIFI_STA_TCP_Client 在站模式STA下实现TCP客户端_sta站点可以作为tcp客户端吗-程序员宅基地

文章浏览阅读9.5k次,点赞5次,收藏16次。开发板购买链接https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111deb2Ij1As&ft=t&id=626366733674开发板简介开发环境搭建 windows基础例程: 0_Hello Bug (ESP_LOGX与printf) 工程模板/打印调试输出 1_LED LED亮灭控制 ..._sta站点可以作为tcp客户端吗

粒子群优化算法(PSO)_psoo-程序员宅基地

文章浏览阅读487次。粒子群优化算法(PSO)package psoo;public class PSO { Particle particles[]; Particle globalBestParticle; Function function; /** * 构造粒子群 * * @param size粒子的个数 */ public PSO(int size, int dim, Fu..._psoo

Three versions of torch.min_torch.min()-程序员宅基地

文章浏览阅读178次。There are three types of torch.min operation. Don’t mix them up.torch.min(input) -----&gt;tensorreturn the min value of all elements in input tensortorch.min(input, dim,keepdim=False,output=None) ..._torch.min()

opencv cuda版本安装_cuda怎么安装opencv-程序员宅基地

文章浏览阅读1.3k次。1.安装英伟达驱动,安装cuda和cudnn2.下载opencv和opencv_contrib,这里有个镜像的下载比较快3.将opencv_contrib放在opencv目录下,并且新建build文件夹,在build文件夹下打开终端并且输入cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=../opencv_contrib-4.4.0/modules_cuda怎么安装opencv

Presto常用函数_presto date_parse-程序员宅基地

文章浏览阅读8k次。Presto 0.216 官方文档Presto 0.216 官方文档-时间1.时间相关函数1.1 :date_parsedate_parse(String,format): 把字符格式的時間格式化為时间戳SELECT date_parse('1995/01/07','%Y/%m/%d')输出结果为:789408000000,即对应的时间为:1995-01-07 00:00:00..._presto date_parse

拼多多接口API申请说明,拼多多根据ID取商品详情原数据 API-程序员宅基地

文章浏览阅读843次,点赞13次,收藏24次。根据推广者备案类型,选择相应的媒体类型。推广位名称,你要在什么地方推广,就填写什么名称,方便区分即可。推广位创建完成,得到PID。完成以上步骤,即可得到:api测试key、密钥、推广链接,分别填写到系统后台即可。4、进入客户端后台系统,即可看到自己key和密钥。3、注册成功后输入相关信息即可登陆,不想输入的可直接选择跳过。2、点击右上角登陆,使用手机号和验证码即可自动注册登陆。请求参数:num_iid=1620002566。"reason": "商品没找到",参数说明:num_iid:商品ID;

随便推点

kibana在linux环境安装实战遇到的问题汇总_kibana连接es超时-程序员宅基地

文章浏览阅读1.9k次。kibana在linux环境安装实战遇到的问题汇总_kibana连接es超时

‘vue‘ 不是内部或外部命令,也不是可运行的程序_vue不是内部或外部命令,也不是可运行的程序-程序员宅基地

文章浏览阅读124次。1、打开cmd执行下面两个命令npm install -g vuenpm install -g @vue/cli2、在电脑里搜索vue.cmd所在路径3、设置系统环境变量_vue不是内部或外部命令,也不是可运行的程序

Open CASCADE基础介绍(4)_open cascade 旋转矩阵-程序员宅基地

文章浏览阅读6.1k次。Open CASCADE基础介绍(4)gp_Trsf类定义一个矩阵变换的类--可以定义平移、旋转、缩放的矩阵;--可以对称于一个点,一条线,一个平面;示例一:对称于一个点:gp_Trsf theTransformation;gp_Pnt PntCenterOfTheTransformation(110,60,60);theTransformation.SetMi_open cascade 旋转矩阵

uva 11383 Golden Tiger Claw km算法-程序员宅基地

文章浏览阅读60次。Problem GGolden Tiger ClawTime Limit: 8 SecondOmi, Raymondo, Clay and Kimiko are on new adventure- in search of new Shen Gong Wu. But Evil Boy Genius Jack Spicer is also there. Omi and J...

VMware内虚拟机自适应及最大化窗口调整方式_vmware窗口最大-程序员宅基地

文章浏览阅读1.2w次。最近在Win10系统安装了VMware12版本客户端,使用这个客户端启动虚拟机却无法完成自适应及最大化调整窗口,最后从别的计算机VMware12客户端中拷贝出的.iso文件解决了问题,这里记录一下VMware常见的几种“自适应及最大化窗口调整修复方式”。打开自适应及最大化窗口调整功能:点击“查看(V)” —> “自动调整大小(A)”菜单栏,选中“自动适应客户机(G)”和“自动适应窗口(W..._vmware窗口最大

上百所大学计算机考研有变化!22计算机考研太难了!-程序员宅基地

文章浏览阅读7.4k次,点赞4次,收藏18次。下面是我发现的今年专业和考试科目有变动的学校(只整理和计算机/软件/网安有关的学校和专业)上海大学1.招生目录新增人工智能研究院,招收电子信息专硕,计算机相关的考试科目数学二,英语二,4..._以后077500就不在招研究生

推荐文章

热门文章

相关标签