1. 前言
本来是想整理一份多线程编程的博客,才发现C++的多线程比Java还要坑。涉及的范围实在有点广,所以之后分开来慢慢讲解,先说这个互斥锁。
首先是互斥,这是什么呢?说起来就又是一张的内容,详细自己去了解一下,参考书籍《操作系统——精髓与设计原理(第七版)》第五章,我这里将要说一下就好了。
互斥就是有一个进程使用了一个临界区的资源,另一个进程就无法使用。举个例子吧,在交通道路上,红灯停绿灯行,当你是红灯的时候,你就需要停下来等待,相反的如果你是绿灯,你就可以通信,这就好比红灯和绿灯是一个信号量,如果有一条道上是绿灯,那么这条道上的车都可以通过,而没有得到绿灯的道就只能等待,这就是互斥。
2. 互斥锁pthread_mutex_t
(1)创建互斥锁
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
pthread_mutexattr_t mut_at;
pthread_mutexattr_init(&mut_at);
pthread_mutex_t mut;
mut = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&mut,&mut_at);
}
创建互斥锁有两种方法,一种就是直接使用PTHREAD_MUTEX_INITIALIZER直接初始化,PTHREAD_MUTEX_INITIALIZER在底层的实现如下:
# define PTHREAD_MUTEX_INITIALIZER \
{ { 0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 } } }
__PTHREAD_SPINS的底层实现如下:
# define __PTHREAD_SPINS 0, 0
说白了,使用这个就是把锁里面的值全部赋值成0。
第二种,调用pthread_mutex_init,其函数原型如下:
/* Initialize a mutex. */
extern int pthread_mutex_init (pthread_mutex_t *__mutex,
const pthread_mutexattr_t *__mutexattr)
__THROW __nonnull ((1));
使用一个pthread_mutexattr_t的指针来初始化pthread_mutex_t
看一下两个结构体的实现就知道,其实这两个基本是一样的,只是pthread_mutex_t比pthread_mutexattr_t多了一个结构体,内部定义如下:
/* 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;
#ifdef __x86_64__
unsigned int __nusers;
#endif
/* KIND must stay at this position in the structure to maintain
binary compatibility. */
int __kind;
#ifdef __x86_64__
short __spins;
short __elision;
__pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV 1
/* Mutex __spins initializer used by PTHREAD_MUTEX_INITIALIZER. */
# define __PTHREAD_SPINS 0, 0
#else
unsigned int __nusers;
__extension__ union
{
struct
{
short __espins;
short __elision;
# define __spins __elision_data.__espins
# define __elision __elision_data.__elision
# define __PTHREAD_SPINS { 0, 0 }
} __elision_data;
__pthread_slist_t __list;
};
#endif
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;
typedef union
{
char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
int __align;
} pthread_mutexattr_t;
这个结构体内部定义了一些比如使用线程的对象标识符,对象等待的队列,锁的属性等数据信息。
(其实我也很纠结这些东西到底是什么东西,想想看一个锁到底需要什么,为什么需要这么多的东西。这个等我深入了解在进行解释)
(2) 锁的作用范围:
互斥锁主要的作用范围有两个:
一个就是用于同一个进程内部线程的同步,对应的为PTHREAD_PROCESS_PRIVATE
另一个是使用在不同进程内部线程的同步,对应的为PTHREAD_PROCESS_SHAPE
如何设置这个变量的值呢?可以参看以下的代码:
int main()
{
pthread_mutexattr_t mut_at;
pthread_mutex_t mut;
pthread_mutexattr_init(&mut_at);
// 将mut_at的作用范围变量设置为PTHREAD_PROCESS_SHARED
pthread_mutexattr_setpshared(&mut_at,PTHREAD_PROCESS_SHARED);
// 将mut_at的作用范围变量设置为PTHREAD_PROCESS_PRIVATE
pthread_mutexattr_setpshared(&mut_at,PTHREAD_PROCESS_PRIVATE);
pthread_mutex_init(&mut,&mut_at);
}
获取这个属性只需要把set改成get,下面是其对应的函数原型:
/* Get the process-shared flag of the mutex attribute ATTR. */
extern int pthread_mutexattr_getpshared (const pthread_mutexattr_t *
__restrict __attr,
int *__restrict __pshared)
__THROW __nonnull ((1, 2));
/* Set the process-shared flag of the mutex attribute ATTR. */
extern int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr,
int __pshared)
__THROW __nonnull ((1));
不知道怎么做的可以看一下原型里面的参数。
(3) 属性:
PTHREAD_MUTEX_TIMED_NP:该属性为缺省属性,就是默认值。当一个线程加锁以后,其余请求锁的线程形成一个等待队列,并在解锁后按优先级获得锁。
PTHREAD_MUTEX_RECURSIVE_NP:嵌套所,该属性允许同一个线程对同一个锁多次获取,并通过unlock多次解锁。如果不同线程请求,则需要在该线程解锁之后竞争。
PTHREAD_MUTEX_ERRORCHECK_NP:检错锁。功能同第一个,只是这个锁在同一个线程请求同一个锁的时候会返回EDEADLK,其他的同第一个,这个只是保证没有任何一个线程获取同一个锁两次以上。
PTHREAD_MUTEX_ADAPTIVE_NP:适应锁。
以上4个为属性,介绍来是怎么设置这些属性呢?这时候就需要以下函数的支持:
/* Return in *KIND the mutex kind attribute in *ATTR. */
extern int pthread_mutexattr_gettype (const pthread_mutexattr_t *__restrict
__attr, int *__restrict __kind)
__THROW __nonnull ((1, 2));
/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_NORMAL,
PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, or
PTHREAD_MUTEX_DEFAULT). */
extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind)
__THROW __nonnull ((1));
一个为获取锁的属性,一个是设置锁的属性。具体使用如下:
pthread_mutexattr_settype(&mut_at,PTHREAD_MUTEX_TIMED_NP);
pthread_mutex_init(&mut,&mut_at);
(4) 锁的操作:
/* Try locking a mutex. */
extern int pthread_mutex_trylock (pthread_mutex_t *__mutex)
__THROWNL __nonnull ((1));
/* Lock a mutex. */
extern int pthread_mutex_lock (pthread_mutex_t *__mutex)
__THROWNL __nonnull ((1));
/* Unlock a mutex. */
extern int pthread_mutex_unlock (pthread_mutex_t *__mutex)
__THROWNL __nonnull ((1));
这四个函数做一下说明:
pthread_mutex_lock:加锁。如果锁已经被占有,则该线程加入一个队列中。
pthread_mutex_trylock:尝试加锁,如果锁已被占有,则线程不加入队列,而是返回错误。
pthread_mutex_unlock:释放锁。
(5) 具体示例:
#include
#include
#include
#include
#include
#include
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
int tf[5];
void* print(void* i)
{
pthread_mutex_lock(&mut);
for(int j=0;j<5;j++)
cout << i << " " << j << endl;
pthread_mutex_unlock(&mut);
}
int main()
{
pthread_t td[5];
for(int i=0;i<5;i++)
tf[i] = i;
for(int i=0;i<5;i++)
pthread_create(&td[i],NULL,print,(void *)&tf[i]);
for(int i=0;i<5;i++)
pthread_join(td[i],NULL);
pthread_mutex_destroy(&mut);
}
这样每一个线程都需要运行到结束才可以释放锁,其他线程才可以进行访问。运行结果如下:
0x602194 0
0x602194 1
0x602194 2
0x602194 3
0x602194 4
0x602190 0
0x602190 1
0x602190 2
0x602190 3
0x602190 4
0x6021a0 0
0x6021a0 1
0x6021a0 2
0x6021a0 3
0x6021a0 4
0x60219c 0
0x60219c 1
0x60219c 2
0x60219c 3
0x60219c 4
0x602198 0
0x602198 1
0x602198 2
0x602198 3
0x602198 4
现在讲这个互斥锁,有错误的话请多多指教。
参考链接:
https://blog.csdn.net/zmxiangde_88/article/details/7998458