我是靠谱客的博主 长情歌曲,最近开发中收集的这篇文章主要介绍线程同步(互斥量)线程同步(互斥量),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

线程同步(互斥量)

互斥量

原理:

  • 使用pthread的互斥接口来保护数据,确保同一时间只有一个线程访问数据。
  • 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行设置(加锁),在访问完成后释放互斥量(解锁)。
  • 对互斥量进行加锁后,其他任何线程试图对互斥量进行再次加锁都会被阻塞,直到当前线程释放该互斥量。
  • 如果释放互斥量时,存在多个线程在阻塞等待,那么所有该锁上的阻塞线程都会变成可运行状态。第一个变为运行状态的线程就可以对互斥量进行加锁,其他线程就会看到互斥量依然是锁着的,然后重新阻塞等待。

创建与销毁:

  • 互斥变量是用pthread_mutex_t数据类型表示的。在使用互斥变量前,需要先对它进行初始化。
  • 如果是静态分配的互斥量,可以把它设置为常量PTHREAD_MUTEX_INITIALIZER。
  • 如果是动态分配的互斥量(例如,通过调用malloc函数),在释放内存前需要调用pthread_mutex_destroy。

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//两个函数的返回值:若成功,返回0;否则,返回错误编码
//要用默认的属性初始化互斥量,只需把attr设为 NULL。

加锁与解锁

对互斥量进行加锁,需要调用pthread_mutex_lock。如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁。
对互斥量解锁,需要调用pthread_mutex_unlock。


#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//所有函数的返回值:若成功,返回0;否则,返回错误编码。

如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。
如果调用pthread_mutex_trylock时,互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞,直接返回0。
如果互斥量已经被锁住了,pthread_mutex_trylock就会失败,不能锁住互斥量,不阻塞,直接返回EBUSY。


实例:


#include <stdlib.h>
#include <pthread.h>
struct foo{
int f_count;
pthread_mutex_t f_lock;
int f_id;
/*...more stuff here... */
};
struct foo *foo_alloc(int id) /* 申请并分配对象空间 */
{
struct foo *fp;
if ((fp = malloc(sizeof(struct foo)))!= NULL){
fp->f_count = 1;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL)!= 0){
free(fp);
return (NULL);
}
/* ... continue initialization ...*/
}
return (fp);
}
void foo_hold(struct foo *fp) /* 对对象增加了一个引用 */
{
pthread_mutex_lock(&fp->f_lock); //因为可能有多个线程创建并持有对象,所以需要加互斥量
fp->f_count++; /* 引用计数加1 */
pthread_mutex_unlock(&fp->f_lock);
}
void foo_rele(struct foo *fp)/* 释放引用对象 */
{
pthread_mutex_lock(&fp->f_lock);
if (--fp->f_count == 0){ /* last reference */
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}else{
pthread_mutex_unlock(&fp->f_lock);
}
}

死锁

  • 如果单个线程试图对同一个互斥量加锁两次,那么它自身就会陷入死锁状态。
  • 不太明显的死锁方式,如果程序中使用一个以上的互斥量时,如果允许一个线程一直占有第一个互斥量,并且试图锁住第二个互斥量时处于阻塞状态,但是这时候拥有第二个互斥量的线程也试图锁住第一个互斥量。两个线程都在请求对方拥有的资源,导致线程都无法继续运行。

避免死锁

  • 可以通过仔细控制互斥量加锁的顺序来避免死锁的发生。

假设需要对两个互斥量A和B同时加锁。如果所有的线程总是在对互斥量B加锁之前锁住互斥量A,那么使用这两个互斥量就不会产生死锁(当然,在其他的资源上仍可能出现死锁)。

  • 可以使用 pthread_mutex_trylock接口避免死锁。

如果已经占有某些锁 而且pthread_mutex_trylock接口返回成功,那么就可以前进。但是,如果不能 获取锁, 可以先释放已经占有的锁, 做好清理工作, 然后过一段时间再重新尝试获取。


具有超时的加锁方式

pthread_mutex_timedlock函数与pthread_mutex_lock是基本等价的,但是在达到超时时间值 时,pthread_mutex_timedlock不会对互斥量进行加锁,而是返回错误码 ETIMEDOUT。


#include < pthread. h>
#include < time. h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
//返回值:若成功,返回0;否则,返回错误编码。

注意:
超时是指定愿意等待的绝对时间(与相对时间对比而言,指定在时间X之前可以阻塞等待,而不是说愿意阻塞 Y秒)。这个超时时间是用timespec结构来表示的,它用秒和纳秒来描述时间。


实例


#include <stdio.h>
#include <pthread.h>
#include <string.h>
int main(){
int err;
struct timespec tout;
struct tm *tmp;
char buf[64];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
printf("mutex is lockedn");
clock_gettime(CLOCK_REALTIME, &tout);
tmp = localtime(&tout.tv_sec);
strftime(buf, sizeof(buf), "%r", tmp);
printf("current time is %sn",buf);
tout.tv_sec += 10; //相对于现在多10s
//注意:这里再次对同一互斥量加锁,造成死锁,等待直到10s超时
err = pthread_mutex_timelock(&lock,&tout);
clock_gettime(CLOCK_REALTIME, &tout);
tmp = localtime(&tout.tv_sec);
strftime(buf, sizeof(buf), "%r", tmp);
printf("the time is now %sn",buf);
if (err == 0) {
printf("mutex locked again!n");
}else{
printf("can't lock mutex again:%sn",strerror(err));
}
}
注意:

Mac OS 10.12.6还不支持pthread_mutex_timelock函数,但是Linux 3.2.0、FreeBSD 8.0以及Solaris 10支持该函数。


最后

以上就是长情歌曲为你收集整理的线程同步(互斥量)线程同步(互斥量)的全部内容,希望文章能够帮你解决线程同步(互斥量)线程同步(互斥量)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(97)

评论列表共有 0 条评论

立即
投稿
返回
顶部