热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

APUE学习之多线程编程(二):线程同步

为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量。常用的锁有互斥量,读写锁,条件变量一、互斥量互斥量是用pthread_m
     为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量。常用的锁有互斥量,读写锁,条件变量           一、互斥量      互斥量是用pthread_mutex_t数据类型表示的,在使用之前,必须对其进行初始化,可以把它设置为PTHREAD_MUTEX_INITIALIZER(只适于静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,最后还要调用pthread_mutex_destroy进行释放。
#include 
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
     要用默认的属性初始化互斥量,只需把attr设为NULL,后面在讨论互斥量属性。      对互斥量进行加锁,使用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞至互斥量解锁,对互斥量解锁,使用pthread_mutex_unlock,如果线程不希望被阻塞,它可以调用pthread_mutex_trylock尝试对互斥量进行加锁,如果互斥量未锁住,则成功加锁,如果互斥量已锁住,pthread_mutex_trylock就会失败,返回EBUSY。
#include 
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
例子:
#include 
#include


struct foo
{
int f_count;
pthread_mutex_t f_lock;
int f_id;
};

struct foo * foo_alloc(int id)
{
struct foo *fp = NULL;

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;
}
}

return fp;
}

void foo_hold(struct foo *fp)
{
pthread_mutex_lock(
&fp->f_lock);
fp
->f_count++;
pthread_mutex_unlock(
&fp->f_lock);
}

void foo_rele(struct foo *fp)
{
pthread_mutex_lock(
&fp->f_lock);

if (--fp->f_count == 0)
{
pthread_mutex_unlock(
&fp->f_lock);
pthread_mutex_destroy(
&fp->f_lock);
free(fp);
}
else
{
pthread_mutex_unlock(
&fp->f_lock);
}
}
View Code
     上面的例子描述了用于保护某个数据结构的互斥量,我们在对象中嵌入引用计数,确保在所有使用该对象的线程完成数据访问之前,该对象的内存空间不会被释放。        如果线程对同一个互斥量加锁两次,那么它自身将陷入死锁状态。如果有一个以上的互斥量,且允许一个线程一直占有第一个互斥量,并且试图锁住第二个互斥量时处于阻塞状态,但是拥有第二个互斥量的线程也在试图锁住第一个互斥量,也阻塞,就死锁了。      可以通过仔细控制互斥量加锁的顺序来避免死锁的发生,譬如要求所有线程必须先锁住互斥量A才能锁住互斥量B。另一种办法是当线程无法获得下一个互斥量的时候,就释放自己已占有的互斥量,过一段时间再试。 例子:
#include "apue.h"
#include


#define NMASH 29
#define HASH(id) (((unsigned long)id) % NMASH)

struct foo *fh[NMASH];

pthread_mutex_t hashlock
= PTHREAD_MUTEX_INITIALIZER;

struct foo
{
int f_count;
pthread_mutex_t f_lock;
int f_id;
struct foo *f_next;
};

struct foo *foo_alloc(int id)
{
struct foo *fp = NULL;
int idx = 0;

if ((fp = malloc(sizeof(struct foo))) != NULL)
{
fp
->f_count = 1;
fp
->f_id = if;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0)
{
free(fp);
return NULL;
}

idx
= HASH(id);
pthread_mutex_lock(
&hashlock);
fp
->f_next = fh[idx];
fh[idx]
= fp;
pthread_mutex_lock(
&fp->f_lock);
pthread_mutex_unlock(
&hashlock);
pthread_mutex_unlock(
&fp->f_lock);
}

return fp;
}

void foo_hold(struct foo *fp)
{
pthread_mutex_lock(
&fp->f_lock);
fp
->f_count++;
pthread_mutex_unlock(
&fp->f_lock);
}

struct foo *foo_find(int id)
{
struct foo *fp = NULL;

pthread_mutex_lock(
&hashlock);

for (fp = fh[HASH(id)]; fp != NULL; fp = fp->next)
{
if (fp->f_id = id)
{
foo_hold(fp);
break;
}
}

pthread_mutex_unlock(
&hashlock);
return fp;
}

void foo_rele(struct foo *fp)
{
struct foo *tfp = NULL;
int idx = 0;

pthread_mutex_lock(
&fp->f_lock);

if (fp->f_count == 1)
{
pthread_mutex_unlock(
&fp->f_lock);
pthread_mutex_lock(
&hashlock);
pthread_mutex_lock(
&fp->f_lock);

if (fp->f_count != 1)
{
fp
->f_count--;
pthread_mutex_unlock(
&hashlock);
pthread_mutex_unlock(
&fp->f_lock);
return;
}

idx
= HASH(fp->f_id);
tfp
= fh[idx];
if (tfp = fp)
{
fh[idx]
= fp->f_next
}
else
{
while(tfp->next != fp)
{
tfp
= tfp->next;
}
tfp
->next = fp->f_next;
}

pthread_mutex_unlock(
&hashlock);
pthread_mutex_unlock(
&fp->f_lock);
pthread_mutex_destroy(
&fp->f_lock);
free(fp);
}
else
{
fp
->f_count--;
pthread_mutex_unlock(
&fp->f_lock);
}
}
View Code
     这个例子比上一个例子多了一个散列表和一个保护散列表的互斥量,加锁的顺序是先hashlock,再f_lock,注意这个顺序,就不会发生死锁,不过这样也导致代码太繁琐,最后一个函数解锁f_lock后重新加锁f_lock,需要重新考察f_count的值,因为可能在这期间被其他线程修改。      这样的方式太复杂,让hashlock也保护f_cout,事情会简单很多。 例子:
#include "apue.h"
#include


#define NMASH 29
#define HASH(id) (((unsigned long)id) % NMASH)

struct foo *fh[NMASH];

pthread_mutex_t hashlock
= PTHREAD_MUTEX_INITIALIZER;

struct foo
{
int f_count;
pthread_mutex_t f_lock;
int f_id;
struct foo *f_next;
};

struct foo *foo_alloc(int id)
{
struct foo *fp = NULL;
int idx = 0;

if ((fp = malloc(sizeof(struct foo))) != NULL)
{
fp
->f_count = 1;
fp
->f_id = if;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0)
{
free(fp);
return NULL;
}

idx
= HASH(id);
pthread_mutex_lock(
&hashlock);
fp
->f_next = fh[idx];
fh[idx]
= fp;
pthread_mutex_lock(
&fp->f_lock);
pthread_mutex_unlock(
&hashlock);
pthread_mutex_unlock(
&fp->f_lock);
}

return fp;
}

void foo_hold(struct foo *fp)
{
pthread_mutex_lock(
&hashlock);
fp
->f_count++;
pthread_mutex_unlock(
&hashlock);
}

struct foo *foo_find(int id)
{
struct foo *fp = NULL;

pthread_mutex_lock(
&hashlock);

for (fp = fh[HASH(id)]; fp != NULL; fp = fp->next)
{
if (fp->f_id = id)
{
foo_hold(fp);
break;
}
}

pthread_mutex_unlock(
&hashlock);
return fp;
}

void foo_rele(struct foo *fp)
{
struct foo *tfp = NULL;
int idx = 0;

pthread_mutex_lock(
&hashlock);

if (fp->f_count == 1)
{

idx
= HASH(fp->f_id);
tfp
= fh[idx];
if (tfp = fp)
{
fh[idx]
= fp->f_next
}
else
{
while(tfp->next != fp)
{
tfp
= tfp->next;
}
tfp
->next = fp->f_next;
}

pthread_mutex_unlock(
&hashlock);
pthread_mutex_destroy(
&fp->f_lock);
free(fp);
}
else
{
fp
->f_count--;
pthread_mutex_unlock(
&hashlock);
}
}
View Code
     当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock互斥量原语允许绑定线程阻塞时间。pthread_mutex_timedlock和pthread_mutex_lock是基本等价的,但是达到超时时间后,pthread_mutex_timedlock会返回。超时时间指原意等待的绝对时间。这个超时时间是用timespec来表示的
#include 
#include

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);

 

     二、读写锁      读写锁与互斥量相似,不过读写锁允许更高的并行性,一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁,简单地来说,就说支持一个写者,多个读者。      当读写锁是写加锁状态时,所以试图对这个锁加锁的线程都会被阻塞,当读写锁在读加锁状态时,所以试图以读模式对它进行加锁的线程都可以得到访问权,但是希望以写模式加锁的线程会被阻塞。不过当有一个线程企图以写模式获取锁时,读写锁会阻塞后面的读模式锁请求,防止读模式锁长期占用。      可知,读写锁适用于对数据结构读的次数远大于写的情况,又称共享互斥锁,读共享,写互斥。
#include 
int pthread_rwlock_init(pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
     读写锁调用phtread_rwlock_init进行初始化,如果希望读写锁有默认的属性,传null给attr即可。      读的模式下锁定读写锁,需要调用phtread_rwlock_rdlock,写的模式下锁定读写锁,需要调用pthread_rwlock_wrlock,不过以何种方式锁定读写锁,都可以调用pthread_rwlock_unlock解锁。
#include 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
例子:
#include 
#include


struct job
{
struct job *j_next;
struct job *j_prev;
pthread_t j_id;
};

struct queue
{
struct job *q_head;
struct job *q_tail;
pthread_rwlock_t q_lock;
};

int queue_init(struct queue *qp)
{
int err;

qp
->q_head = NULL;
qp
->q_tail = NULL;
err
= pthread_rwlock_init(&qb->q_lock, NULL);
if (err != 0)
{
return err;
}

return 0
}

void job_insert(struct queue *qp, struct job *jp)
{
pthread_rwlock_wrlock(
&qb->q_lock);
jp
->next = qp->head;
jp
->j_prev = NULL;

if (qp->q_head != NULL)
{
qp
->q_head->j_prev = jp;
}
else
{
qp
->tail = jp;
}
qp
->head = jp;
pthread_rwlock_unlock(
&qp->q_lock);
}

void job_append(struct queue *qp, struct job *jp)
{
pthread_rwlock_wrlock(
&qp->q_lock);
jp
->j_next = NULL;
jp
->j_prev = qp->tail;
if (qp->q_tail != NULL)
{
qp
->q_tail->j_next = jp;
}
qp
->q_tail = jp;
pthread_rwlock_unlock(
&qp->q_lock);
}

void job_remove(struct queue *qp, struct job *jp)
{
pthread_rwlock_wrlock(
&qp->q_lock);
if (jp == qp->q_head)
{
qp
->q_head = jp->j_next;
if (qp->q_tail == jp)
{
qp
->tail = NULL;
}
else
{
jp
->next->j_prev = jp->j_prev;
}
}
else if (jp == qp->q_tail)
{
qp
->q_tail = jp->j_prev;
jp
->j_prev->j_next = NULL;
}
else
{
jp
->j_prev->j_next = jp->j_next;
jp
->j_next->j_prev = jp->j_prev;
}
pthread_rwlock_unlock(
&qp->q_lock);
}

struct job *job_find(struct queue *qp, pthread_t id)
{
struct job *jp;

if (pthread_rwlock_rdlock(&qp->q_lock) != 0)
{
return NULL;
}

for (jp = qb->q_head; jp != NULL; jp = jp->j_next)
{
if (pthread_equal(jp->j_id, id))
{
break;
}
}
pthread_rwlock_unlock(
&qp->q_lock);
return jp;
}
View Code
     与互斥量一样,读写锁也有带超时的读写锁函数,避免陷入永久的阻塞。
#include 
#include

int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);

 

     三、条件变量      条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。      条件本身由互斥量保护,线程在改变条件状态之前必须锁定互斥量。在使用条件变量之前,必须把它初始化,可以把常量PTHREAD_CON_INITIALIZE赋给静态分配的条件变量,也可用pthread_cond_init函数进行初始化。使用pthread_cond_destroy释放。
#include 
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_con_t *cond);
     如果需要一个默认属性的条件变量,把null给attr即可。      我们使用pthread_cond_wait等待条件变量为真,如果在给定时间内不能满足,则返回错误码。
#include
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)
int pthread_cond_timedwait(pthread_cond_t *restrict cond, phtread_mutex_t *restrict mutex, const struct timespec *restrict tsptr)
     调用者把锁定的互斥量传给函数,函数自动把调用线程放到等待条件的线程列表上,对互斥量解锁,当pthread_cond_wait返回时,互斥量再次被锁住。pthread_cond_timedwait多了原意等待的时间。      有两个函数可用于通知线程条件已满足,pthread_cond_signal函数至少唤醒一个,pthread_cond_broadcast唤醒等待该条件的所有线程。
#include
int pthread_cond_signal(pthread_cond_t *cond)
int pthread_cond_broadcast(pthread_cond_t *cond)
例子:
#include 

struct msg
{
struct msg *m_next;
};

struct msg *workq;

pthread_cond_t qready
= PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock
= PTHREAD_MUTEX_INITIALIZER;

void process_msg(void)
{
struct msg *mp;

for(;;)
{
pthread_mutex_lock(
&qlock);
while (workq == NULL)
{
pthread_cond_wait(
&qready, &qlock);
}

mp
= workq;
workq
= mp->m_next;
pthread_mutex_unlock(
&qlock);
}
}

void enqueue_msg(struct msg *mp)
{
pthread_mutex_lock(
&qlock);
mp
->m_next = workq;
workq
= mp;
pthread_mutex_unlock(
&qlock);
pthread_cond_signal(
&qready);
}
View Code

推荐阅读
  • Linux线程的同步和互斥
    目录1、线程的互斥2、可重入VS线程安全3、线程的同步1、线程的互斥 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文主要介绍了gym102222KVertex Covers(高维前缀和,meet in the middle)相关的知识,包括题意、思路和解题代码。题目给定一张n点m边的图,点带点权,定义点覆盖的权值为点权之积,要求所有点覆盖的权值之和膜qn小于等于36。文章详细介绍了解题思路,通过将图分成两个点数接近的点集L和R,并分别枚举子集S和T,判断S和T能否覆盖所有内部的边。文章还提到了使用位运算加速判断覆盖和推导T'的方法。最后给出了解题的代码。 ... [详细]
  • 本文介绍了一种求解最小权匹配问题的方法,使用了拆点和KM算法。通过将机器拆成多个点,表示加工的顺序,然后使用KM算法求解最小权匹配,得到最优解。文章给出了具体的代码实现,并提供了一篇题解作为参考。 ... [详细]
  • 本文介绍了如何使用MATLAB调用摄像头进行人脸检测和识别。首先需要安装扩展工具,并下载安装OS Generic Video Interface。然后使用MATLAB的机器视觉工具箱中的VJ算法进行人脸检测,可以直接调用CascadeObjectDetector函数进行检测。同时还介绍了如何调用摄像头进行人脸识别,并对每一帧图像进行识别。最后,给出了一些相关的参考资料和实例。 ... [详细]
  • 学习笔记17:Opencv处理调整图片亮度和对比度
    一、理论基础在数学中我们学过线性理论,在图像亮度和对比度调节中同样适用,看下面这个公式:在图像像素中其中:参数f(x)表示源图像像素。参数g(x)表示输出图像像素。 ... [详细]
author-avatar
手机用户2602933853
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有