POSIX多线程互斥量及其应用

POSIX多线程互斥量及其应用

 

pthread_mutex_t  _mutex = PTHREAD_MUTEX_INITIALIZER;                      

int pthread_mutex_init( pthread_mutex_t  *mutex,  pthread_mutex  attr_t  *attr );

int pthread_mutex_destroy (pthread_mutex_t  *mutex);

 

程序中的互斥量是用pthread_mutex_t类型的变量来表示的,不能拷贝互斥量变量,因为是用拷贝的互斥量是不确定的。但是可以拷贝指向互斥量的指针,这样就可以使多个函数或线程共享互斥量来实现同步。

创建和销毁互斥量:

互斥量一般声明为两种类型,在函数体外、文件范围内使用声明为静态类型;如果有其他文件使用则声明为外部类型。当使用malloc动态分配一个包含互斥量的数据结构时,通常不采用静态方式(①)初始化一个互斥量,此时应使用pthread_mutex_init(②)来动态初始化静态类型的互斥量。如果要动态初始化静态类型的互斥量,则必须保证每个互斥量在使用前被初始化且只能被初始化一次。当初始化一个非缺省属性的互斥量时,则必须使用动态初始化。

当不再需要一个通过pthread_mutex_init动态初始化的互斥量时,应调用int pthread_mutex_destroy来释放它。而使用PTHREAD_MUTEX_INITIALIZER宏初始化的互斥量则不需要被释放。

 

int pthread_mutex_lock    (pthread_mutex_t  *mutex);

int pthread_mutex_trylock  (pthread_mutex_t  *mutex);

int pthread_mutex_unlock  (pthread_mutex_t  *mutex);

 

 

互斥量的加锁与解锁:

在最简单情况下使用互斥量时容易的事情:通过调用pthread_mutex_lock或pthread_mutex_trylock锁住互斥量,处理共享数据,然后调用pthread_mutex_unlock解锁互斥量。

当调用线程已经锁住互斥量之后,就不能再加锁该互斥量。试图这样做的结果可能是返回错误(EDEADLK)或者可能陷入“自死锁”,使线程永远等待下去。不能解锁一个已经解锁的互斥量,也不能解锁由其他线程锁住的互斥量。被锁住的互斥量是属于加锁线程的。

 

接下来贴一段使用互斥量版本的闹钟例子(运行环境为ubuntu + Qt):

 

#include <QCoreApplication>

#include <pthread.h>

#include <time.h>

#include "errors.h"

#include <QDebug>

 

 

typedef struct alarm_tag {

    struct alarm_tag    *link;

    int                 seconds;

    time_t              time;   /* seconds from EPOCH */

    char                message[64];

} alarm_t;

 

pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;

alarm_t *alarm_list = NULL;

 

void *alarm_thread (void *arg)

{

    alarm_t *alarm;

    int sleep_time;

    time_t now;

    int status;

 

    while (1) {

        status = pthread_mutex_lock (&alarm_mutex);

        if (status != 0)

            err_abort (status, "Lock mutex");

        alarm = alarm_list;

 

        if (alarm == NULL)

            sleep_time = 1;

        else {

            alarm_list = alarm->link;

            now = time (NULL);

            if (alarm->time <= now)

                sleep_time = 0;

            else

                sleep_time = alarm->time - now;

#ifdef DEBUG

            printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,

                sleep_time, alarm->message);

#endif

            }

 

        status = pthread_mutex_unlock (&alarm_mutex);

        if (status != 0)

            err_abort (status, "Unlock mutex");

        if (sleep_time > 0)

            sleep (sleep_time);

        else

            sched_yield ();

 

        if (alarm != NULL) {

            printf ("(%d) %s\n", alarm->seconds, alarm->message);

            free (alarm);

        }

    }

}

 

int main(int argc, char *argv[])

{

//    QCoreApplication a(argc, argv);

    int status;

        char line[128];

        alarm_t *alarm, **last, *next;

        pthread_t thread;

 

        status = pthread_create (

            &thread, NULL, alarm_thread, NULL);

        if (status != 0)

            err_abort (status, "Create alarm thread");

        while (1) {

            printf ("alarm> ");

            if (fgets (line, sizeof (line), stdin) == NULL) exit (0);

            if (strlen (line) <= 1) continue;

            alarm = (alarm_t*)malloc (sizeof (alarm_t));

            if (alarm == NULL)

                errno_abort ("Allocate alarm");

 

            if (sscanf (line, "%d %64[^\n]",

                &alarm->seconds, alarm->message) < 2) {

                fprintf (stderr, "Bad command\n");

                free (alarm);

            } else {

                status = pthread_mutex_lock (&alarm_mutex);

                if (status != 0)

                    err_abort (status, "Lock mutex");

                alarm->time = time (NULL) + alarm->seconds;

 

                last = &alarm_list;

                next = *last;

                while (next != NULL) {

                    if (next->time >= alarm->time) {

                        alarm->link = next;

                        *last = alarm;

                        break;

                    }

                    last = &next->link;

                    next = next->link;

                }

 

                if (next == NULL) {

                    *last = alarm;

                    alarm->link = NULL;

                }

    #ifdef DEBUG

                printf ("[list: ");

                for (next = alarm_list; next != NULL; next = next->link)

                    printf ("%d(%d)[\"%s\"] ", next->time,

                        next->time - time (NULL), next->message);

                printf ("]\n");

    #endif

                status = pthread_mutex_unlock (&alarm_mutex);

                if (status != 0)

                    err_abort (status, "Unlock mutex");

            }

        }

 

//    return a.exec();

}

 

上面程序是对多线程版本闹钟程序的改进。

简单理下程序结构:

alarm_t结构体包含了一个标准UNIX  time_t类型的绝对时间,表示从UNIX纪元(1970年1月1日 00:00)开始到闹铃时的秒数。线程函数依次处理alarm_list 中每个闹钟的请求,线程永不停止,当main函数返回时,线程“政蒸发”。如果列表中没有闹钟请求,则线程阻塞自己1秒,解锁互斥量,以便主线程可以添加新的闹钟请求。

在线程睡眠或阻塞之前,总要解锁互斥量。如果互斥量仍被锁住,则主线程即就无法向列表中添加请求,这将使程序变成同步工作方式。调用sched_yield则变得不同,现在只需简单理解为将处理器交给另一个等待运行的线程。

主函数程序与之前差别不大,主要是讲闹钟请求排序后加入闹钟结构体中。

该改进版本有几个缺点。虽然与多进程版本和多线程版本相比,该版本占用的资源很少,但是响应性不够好。改进的方法是使用条件变量来通知共享数据状态的变化,之后我们会有说明。

 

如果大家觉得还阔以,欢迎大家留言交流。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值