分享

互斥量 mutex

 wusiqi111 2017-12-07

1.1 基本概念

为了确保同一时间只有一个线程访问数据,在访问共享资源前需要对互斥量上锁。一旦对互斥量上锁后,任何其他试图再次对互斥量上锁的线程都会被阻塞,即进入等待队列

上面的文字用伪代码表示:

lock(&mutex);
// 访问共享资源
unlock(&mutex);
  • 1
  • 2
  • 3

有些同学可能觉得通过代码很容易实现,其实不然。比方说下面这样:

// 加锁
if (flag == 0) {
  flag == 1;
}
// 访问共享资源
// 解锁
flag == 0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面这种做法是错误的,你有没有想过,标记 flag 也是共享资源?

实际上,有一种称之为 peterson 的算法可以解决两线程互斥问题,它的原理并不容易,如果你对此有兴趣,请参考《深入理解互斥锁的实现》

1.2 互斥量的数据类型

pthread 中,互斥量是用 pthread_mutex_t 数据类型表示的,通常它是一个结构体。在使用它前,必须先对它进行初始化。有两种方法可以对它进行初始化:

  • 通过静态分配的方法,将它设置为常量 PTHREAD_MUTEX_INITIALIZER.
  • 使用函数 pthread_mutex_init 进行初始化,如果是用此种方法初始化的互斥量,用完后还需要使用 pthread_mutex_destroy 对其进行回收。
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
  const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutext_t *mutex);
  • 1
  • 2
  • 3

在上面的函数中,有两点需要提一下:

  • (1) restrict 关键字的含义:访问指针 mutex 指针的内容的唯一方法是使用 mutex 指针。通常这是告诉编译器:除了 mutex 指针指向这个内存,再也没别的指针指向这里了。
  • (2) pthread_mutexattr_t 类型,用来描述互斥量的属性。现阶段,attr 指针默认设置为 NULL.

1.3 互斥量的加锁和解锁

// 用于加锁的两个函数
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);

// 解锁只有下面这一种方法
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在第 1.1 节中说,如果试图对一个已加锁的互斥量上锁,会让线程阻塞进入等待队列,实际上这是对 pthread_mutex_lock 函数说的。

如果使用 pthread_mutex_trylock,无论互斥量之前有没有上锁,线程会立即返回而不会阻塞,它是通过返回值来判断是否上锁成功:

  • 如果 pthread_mutex_trylock 返回 0, 表示上锁成功。
  • 如果 pthread_mutex_trylock 返回 EBUSY,表示上锁失败。

所以这两种上锁的函数唯一区别就是一个是阻塞函数,另一个是非阻塞函数。不过通常不使用非阻塞版本的,它会浪费 cpu,除非你别有用意。

有同学可能会好奇,为什么我们自己用一个共享的 flag 变量做标记不行,而这里的 lock 函数却可以做到?这是因为 lock 函数对 mutex 的操作是原子的,所谓的原子操作,就是要么一次执行成功,要么一次执行失败。而你使用全局 flag 变量,是做不到这一点的,从 if (flag == 0) 到 flag = 1的赋值操作是分成了两个步骤,翻译成汇编语句那就需要更多条了。所以如果你想自己实现这样的原子操作,就只能使用汇编语句来编写啦,有可能的话,后面我自己用代码来实现一个!(当然用关中断也可以,只不过没必要如此麻烦。)

示例:

#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

struct UpgradeParamSet
{
char value[1024];
int length;

};
struct UpgradeParamSet parambak;
//int gflag = 0;
 void* UpgradeParamSet_tcp(void* data)
{
struct UpgradeParamSet param;
memset(&param,0,sizeof(struct UpgradeParamSet));
memcpy(&param, (void*)data, sizeof(struct UpgradeParamSet));
printf("UpgradeParamSet_tcp param.value=%s,param.length=%d\r\n",param.value,param.length);
//sySendMsgToUpgradeClient(param.value,param.length);
return NULL;
}

void UpgradeParamSet_thread(const char *value)
{
pthread_mutex_lock(&lock);
    int nRet = -1;
    pthread_t   clientTid;
//struct UpgradeParamSet param;
memset(&parambak,0, sizeof(struct UpgradeParamSet));
if(strlen(value)>1023){
strncpy(parambak.value,value,1023);
}else{
strcpy(parambak.value,value);
}
parambak.length = strlen(parambak.value);
printf("UpgradeParamSet_thread parambak.value=%s,parambak.length=%d\r\n",parambak.value,parambak.length);

    nRet = pthread_create(&clientTid, NULL,(void*)UpgradeParamSet_tcp, &(parambak));
    if (nRet != 0){
        printf("[%s] Failed to create UpgradeParamSet_thread\n", __FUNCTION__);
      
    } else {
        pthread_detach(clientTid);
    }
pthread_mutex_unlock(&lock);
}



int main(){
printf("main start\r\n");
char msg1[1024]="hello ni hao";
char msg2[1024]="123456789";
char msg3[1024]="987654321";
char msg4[1024]="abcdef";
/* while(gflag == 1){
sleep(1);
}
UpgradeParamSet_thread(msg1);
while(gflag == 1){
sleep(1);
}
UpgradeParamSet_thread(msg2);
while(gflag == 1){
sleep(1);
}
UpgradeParamSet_thread(msg3);
while(gflag == 1){
sleep(1);
}
UpgradeParamSet_thread(msg4);
*/
UpgradeParamSet_thread(msg1);
UpgradeParamSet_thread(msg2);
UpgradeParamSet_thread(msg3);
UpgradeParamSet_thread(msg4);
return 0;

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多