linux c编程:互斥锁
们常说互斥锁保护临界区,实际上是说保护临界区中被多个线程或进程共享的数据。互斥锁保证任何时刻只有一个线程
在执行其中的代码。
互斥锁具有以下特点:
·
原子性:把一个互斥锁定义为一个原子操作,这意味着操作系统保证了如果一个线程锁定了互斥锁,则没有其他线程可以在同一时间成功锁定这个互斥量。
·
唯一性:如果一个线程锁定一个互斥量,在它接触锁定之前,没有其他线程可以锁定这个互斥量。
·
非繁忙等待:如果一个线程已经锁定了一个互斥锁,第二个线程又试图去锁定这个互斥锁,则第二个线程将被挂起(不占用
CPU
资源),直到第一个线程解锁,第二个线程则被唤醒并继续执行,同时锁定这个互斥量
创建互斥锁需要用到下面几个函数和变量:
在使用互斥锁之前,需要先创建一个互斥锁的对象。
互斥锁的类型是 pthread_mutex_t
,所以定义一个变量就是创建了一个互斥锁。
pthread_mutex_t mtx;
这就定义了一个互斥锁。但是如果想使用这个互斥锁还是不行的,我们还需要对这个互斥锁进行初始化, 使用函数 pthread_mutex_init() 对互斥锁进行初始化操作。
pthread_mutex_init(&mtx, NULL);
除了使用 pthread_mutex_init() 初始化一个互斥锁,我们还可以使用下面的方式定义一个互斥锁:
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
在头文件 /usr/include/pthread.h 中,对 PTHREAD_MUTEX_INITIALIZER 的声明如下
# define PTHREAD_MUTEX_INITIALIZER \
{ { , , , , , , { , } } }
为什么可以这样初始化呢,因为互斥锁的类型 pthread_mutex_t 是一个联合体, 其声明在文件 /usr/include/bits/pthreadtypes.h 中,代码如下:
/* Data structures for mutex handling. The structure of the attribute
type is not exposed on purpose. */
typedef union
{
struct __pthread_mutex_s
{
int __lock;
unsigned int __count;
int __owner;
#if __WORDSIZE == 64
unsigned int __nusers;
#endif
/* KIND must stay at this position in the structure to maintain
binary compatibility. */
int __kind;
#if __WORDSIZE == 64
int __spins;
__pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV
#else
unsigned int __nusers;
__extension__ union
{
int __spins;
__pthread_slist_t __list;
};
#endif
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t; 获取互斥锁:
接下来是如何使用互斥锁进行互斥操作。在进行互斥操作的时候, 应该先"拿到锁"再执行需要互斥的操作,否则可能会导致多个线程都需要访问的数据结果不一致。 例如在一个线程在试图修改一个变量的时候,另一个线程也试图去修改这个变量, 那就很可能让后修改的这个线程把前面线程所做的修改覆盖了。
下面是获取锁的操作:
阻塞调用
pthread_mutex_lock(&mtx);
这个操作是阻塞调用的,也就是说,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。
非阻塞调用
如果不想阻塞,而是想尝试获取一下,如果锁被占用咱就不用,如果没被占用那就用, 这该怎么实现呢?可以使用 pthread_mutex_trylock() 函数。 这个函数和 pthread_mutex_lock() 用法一样,只不过当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。
int err = pthread_mutex_trylock(&mtx);
if( != err) {
if(EBUSY == err) {
//The mutex could not be acquired because it was already locked.
}
}
超时调用
如果不想不断的调用 pthread_mutex_trylock() 来测试互斥锁是否可用, 而是想阻塞调用,但是增加一个超时时间呢,那么可以使用 pthread_mutex_timedlock() 来解决, 其调用方式如下:
struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL) + ;
abs_timeout.tv_nsec = ; int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if( != err) {
if(ETIMEDOUT == err) {
//The mutex could not be locked before the specified timeout expired.
}
}
上面代码的意思是,阻塞等待线程锁,但是只等1秒钟,一秒钟后如果还没拿到锁的话, 那就返回,并返回一个错误代码 ETIMEDOUT,意思是超时了。
其中 timespec 定义在头文件 time.h 中,其定义如下
struct timespec
{
__time_t tv_sec; /* Seconds. */
long int tv_nsec; /* Nanoseconds. */
};
释放互斥锁
用完了互斥锁,一定要记得释放,不然下一个想要获得这个锁的线程, 就只能去等着了,如果那个线程很不幸的使用了阻塞等待,那就悲催了。
释放互斥锁比较简单,使用 pthread_mutex_unlock() 即可:
pthread_mutex_unlock(&mtx);
同步中有一个称为生产者
-
消费者(
producer-consumer
)的经典问题。一个或多个生产者(线程或进程)产生一个个数据条目,这些条目由一个或多个消费者(线程或进程)处理。数据条目在生产者和消费者之间通过某种类型的
IPC
传递。
这里以生产者和消费者为例,模型如下:
根据这个模型
可以构造相关的代码:
#include <pthread.h>
#include <stdio.h>
#define
MAXNITEMS 1000000
#define
MAXNTHREADS 5
int
nitems;
struct
{
pthread_mutex_t
mutex;
int
buff[MAXNITEMS];
int
nput;
int
nval;
}shared={PTHREAD_MUTEX_INITIALIZER};
void
*produce(void *arg)
{
for(;;)
{
pthread_mutex_lock(&shared.mutex);
if(shared.nput
>= nitems)
{
pthread_mutex_unlock(&shared.mutex);
return
NULL;
}
shared.buff[shared.nput]=shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex);
*((int
*)arg)+=1;
}
}
void
*consumer(void *arg)
{
int
i;
for(i=0;i<=nitems;i++)
{
if(shared.buff[i]
!= i)
printf("buff[%d]=%d\n",i,shared.buff[i]);
}
return
NULL;
}
int
produce_consumer()
{
int
i,nthreads,count[MAXNTHREADS];
pthread_t
tid_produce[MAXNTHREADS],tid_consume;
nitems=MAXNITEMS;
nthreads=MAXNTHREADS;
pthread_setconcurrency(nthreads);
for(i=0;i<nthreads;i++)
{
count[i]=0;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
}
for(i=0;i<nthreads;i++)
{
pthread_join(tid_produce[i],NULL);
printf("count[%d]=%d\n",i,count[i]);
}
pthread_create(&tid_consume,NULL,consumer,NULL);
pthread_join(tid_consume,NULL);
return
1;
}
1
首先我们创建生产者线程,每个线程执行
produce
。在
tid_produce
数组中保存每个线程的线程
ID
。传递给每个生产者线程的参数是指向
count
数组中某个元素的指针。首先把该计数器初始化为
0.
然后每个线程在每次往缓冲区中存放一个条目时给这个计数器加
1
。当一切生成完毕时,我们输出这个计数器数组各元素的值,以查看每个生产者线程分别存放了多少条目
2
等待所有生产者线程终止,同时输出每个线程的计数器值,此后才启动单个消费者线程
程序编译报错:
pthread.c:(.text+0x85)
:对‘
pthread_create’
未定义的引用。
由于
pthread
库不是
Linux
系统默认的库,连接时需要使用库
libpthread.a
、
codeblocks
编译器中加入
libpthread.so
的共享库
或者在用GCC编译程序的时候加上-lpthread
参数
:
程序运行结果如下
:
linux c编程:互斥锁的更多相关文章
- (转)Linux C 多线程编程----互斥锁与条件变量
转:http://blog.csdn.net/xing_hao/article/details/6626223 一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1. 初始化: 在 ...
- linux 2.6 互斥锁的实现-源码分析
http://blog.csdn.net/tq02h2a/article/details/4317211 看了看linux 2.6 kernel的源码,下面结合代码来分析一下在X86体系结构下,互斥锁 ...
- Linux再谈互斥锁与条件变量
原文地址:http://blog.chinaunix.net/uid-27164517-id-3282242.html pthread_cond_wait总和一个互斥锁结合使用.在调用pthread_ ...
- Linux系统编程 —互斥量mutex
互斥量mutex 前文提到,系统中如果存在资源共享,线程间存在竞争,并且没有合理的同步机制的话,会出现数据混乱的现象.为了实现同步机制,Linux中提供了多种方式,其中一种方式为互斥锁mutex(也称 ...
- linux线程及互斥锁
进程是资源管理的最小单元,线程是程序执行的最小单元.在操作系统的设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销. 就像进程有一个PID一样,每个线程也有 ...
- python 使用多线程进行并发编程/互斥锁的使用
import threading import time """ python的thread模块是比较底层的模块,python的threading模块是对thread做了 ...
- 并发编程---互斥锁---互斥锁与join的区别
互斥锁 互斥锁:就是把多个进程并发,修改成一块共享数据的操作变成串行,保证是一个一个来修改的. 缺点:效率低,加锁过程复杂 优点:增加了安全性 from multiprocessing import ...
- c++多线程编程互斥锁初步
上一次讲述了多线程编程,但是由于线程是共享内存空间和资源的,这就导致:在使用多线程的时候,对于共享资源的控制要做的很好.先上程序: #include <iostream> #include ...
- pthread_mutex_init & 互斥锁pthread_mutex_t的使用
pthread_mutex_init l 头文件: #include <pthread.h> l 函数原型: int pthread_mutex_init( ...
- 互斥锁pthread_mutex_init()函数
linux下为了多线程同步,通常用到锁的概念.posix下抽象了一个锁类型的结构:ptread_mutex_t.通过对该结构的操作,来判断资源是否可以访问.顾名思义,加锁(lock)后,别人就无法打开 ...
随机推荐
- iOS 11确认将完全停止支持 32 位应用
苹果正在逐渐淘汰 32 位应用,而且会在今年秋天完成.根据知名开发者 Steven Troughton-Smith 透露,苹果会在 iOS 11 发布后,停止支持 32 位应用.这意味着 App St ...
- Windows管理多个java版本--解决'has value '1.8',but'1.7' is required'的方法
公司考虑到代码的可持续维护性,要求全部使用java7,自己又想在空闲时间学一些java8的新特性,故在安装完1.7之后又安装了1.8,导致eclisp在启动时报’has value ‘1.8’,but ...
- Entity简单使用
urlEntity: //定义 package com.example.cc.ecustapp.Model; /** * Created by weijiawang on 2016/3/8. */pu ...
- golang模板语法简明教程
[模板标签] 模板标签用"{{"和"}}"括起来 [注释] {{/* a comment */}} 使用“{{/*”和“*/}}”来包含注释内容 [变量 ...
- Java泛型小结
本来想着写一篇总结Java泛型的文章,但是却发现了一篇不错的博文,引用自: http://www.cnblogs.com/lwbqqyumidi/p/3837629.html#undefined 现摘 ...
- Dubbo—Zookeeper的典型应用
1.Zookeeper 作为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有 H ...
- sds(简单动态字符串) 内存预分配优化策略
* 1024 , 也就是说. 当大小小于 1MB 的字符串运行追加操作时,sdsMakeRoomFor 就为它们分配多于所需大小一倍的空间: 当字符串的大小大于 1MB . 那么 sdsMakeRoo ...
- php ocket通信机制
php 实例说明 socket通信机制 一,socket是什么 什么是socket 所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通 ...
- 长尾分布,重尾分布(Heavy-tailed Distribution)
Zipf分布: Zipf分布是一种符合长尾的分布: 就是指尾巴很长的分布.那么尾巴很长很厚的分布有什么特殊的呢?有两方面:一方面,这种分布会使得你的采样不准,估值不准,因为尾部占了很大部分.另一方面, ...
- Anaconda2+Theano 安装过程中的所有的坑。。。
写在前面的废话 上次搞theano安装还是一年多以前..anaconda才出到1.4,当时的AnacondaCE,直接安装完基本上theano啥的都一套成功.. 今天换了个电脑,重装anaconda, ...