多线程互斥锁 pthread_mutex 的使用及初始化问题

  • Post author:
  • Post category:其他


一、互斥锁pthread_mutex的使用

1、初始化锁

有两种方法初始化互斥锁,静态方式和动态方式。


静态方式:


pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER;

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


动态方式:


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

其中mutexattr用于指定互斥锁属性如下,如果为

NULL

则使用缺省属性。


PTHREAD_MUTEX_TIMED_NP

,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。


PTHREAD_MUTEX_RECURSIVE_NP

,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。


PTHREAD_MUTEX_ERRORCHECK_NP

,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。


PTHREAD_MUTEX_ADAPTIVE_NP

,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

2、锁的使用

int pthread_mutex_lock(pthread_mutex_t *mutex)  //加锁,获取不到锁会挂起

int pthread_mutex_unlock(pthread_mutex_t *mutex)  //解锁

int pthread_mutex_trylock(pthread_mutex_t *mutex) //在锁已经被占据时返回EBUSY而不是挂起等待。

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

3、代码示例

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
 
void *function(void *arg);
pthread_mutex_t mutex;
int counter = 0;
int main(int argc, char *argv[])
{
    int rc1,rc2;
     
    char *str1="wenhaoll";
    char *str2="linglong";
    pthread_t thread1,thread2;
 
    pthread_mutex_init(&mutex,NULL);
    if((rc1 = pthread_create(&thread1,NULL,function,str1)))
    {
        fprintf(stdout,"thread 1 create failed: %d\n",rc1);
    }
 
    if(rc2=pthread_create(&thread2,NULL,function,str2))
    {
        fprintf(stdout,"thread 2 create failed: %d\n",rc2);
    }
 
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    return 0;
}
 可以尝试将关于锁的操作去掉,有什么效果
void *function(void *arg)
{
    char *m;
    m = (char *)arg;
    pthread_mutex_lock(&mutex);
    while(*m != '\0')
    {
        printf("%c",*m);
        fflush(stdout);
        m++;
        sleep(1);
    }
    printf("\n");
    pthread_mutex_unlock(&mutex);
}

二、调试遇到的初始化问题

问题描述,在锁初始化时,看到很多应用没有初始化,直接使用,如下示例

pthread_mutex_t log_lock;
 
 
void write1()
{
	char buf[]="111111111111111111111111111111111\n";
	int num=0;
	while(1)
		{
		    pthread_mutex_lock(&log_lock);
 
			//fwrite(buf,sizeof(buf),1,write_fd);
			fprintf(write_fd, buf);
			//write(write_fd,buf,strlen(buf));
			if(++num>=10000)
				{
				printf("1 ok\n");
				pthread_mutex_unlock(&log_lock);
				return;
			}
 
		    pthread_mutex_unlock(&log_lock);
		    usleep(10);
	}
}
int main()
{
    pthread_t write1_id;
	//write_fd = open("./testwrite",O_WRONLY | O_APPEND);
	write_fd = fopen("./testwrite","a+");
	if(write_fd<0)
	 {
	  perror("open");
	  exit(1);
	 }
	//pthread_mutex_init(&log_lock,NULL);
 
	pthread_create(&write1_id,NULL,write1,NULL);
	while(1)
		{
		sleep(1);
	}
		
	return 0;
}

说好的使用前先初始化,但是没有初始化锁也可以正常使用,why?

于是我尝试用gdb跟了一下,我发现这三种方式

  • pthread_mutex_t log_lock; //直接使用
  • pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
  • pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

打印出来的pthread_mutex_t结构信息都是一模一样的都是如下:

(gdb) p log_lock
$1 = {__data = {__lock = 0, __count = 0, __owner = 0, __kind = 0, __nusers = 0, {__spins = 0, __list = {__next = 0x0}}}, 
  __size = '\000' <repeats 23 times>, __align = 0}

所以是不是可以说不初始化也可以正常使用锁,如果不初始化使用锁,会有什么样的影响么?

我们来分析一下为什么会这样,然后给出使用建议。


pthread_mutex_t log_lock; //直接使用,



由于是全局变量,编译后结构体成员默认值为0


pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;

这个宏实际定义为

# define PTHREAD_MUTEX_INITIALIZER  { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }


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



这个初始化函数没有仔细追代码,猜想也是初始化成员为0


最后建议使用锁时尽量初始化,按照标准流程来。



版权声明:本文为u013253075原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。