`

linux c学习笔记----线程同步

阅读更多

1.互斥量


互斥变量用pthead_mutex_t数据类型来表示,在使用互斥变量之前,必须首先对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以能过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc)函数,那么释放内存前需要使用pthread_mutex_destroy.


int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexarrt_t * restrict arrt);

int pthread_mutex_destroy(pthread_mutex_t &mutex);

返回值:成功返回0,否则返回错误编号。

要用默认的属性初始化互斥量,只需把attr设置为NULL。


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,否则返回错误编号。



#include <stdlib.h>
#include <pthread.h>

struct foo {
	int             f_count;
	pthread_mutex_t f_lock;
	/* ... more stuff here ... */
};

struct foo *
foo_alloc(void) /* allocate the object */
{
	struct foo *fp;

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

void
foo_hold(struct foo *fp) /* add a reference to the object */
{
	pthread_mutex_lock(&fp->f_lock);
	fp->f_count++;
	pthread_mutex_unlock(&fp->f_lock);
}

void
foo_rele(struct foo *fp) /* release a reference to the object */
{
	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);
	}
}

 2.避免死锁

如果线程试图对同一个互斥量加锁两次,那么它自身就会陷入死锁状态。

一个线程试图以与另一个线程相反的顺序销售互斥量时,才可能出现死锁。

实例:



#include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <pthread.h>
   #include <errno.h>
   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
   int lock_var;
   time_t end_time;
   void pthread1(void *arg);
   void pthread2(void *arg);
   int main(int argc, char *argv[])
  {
    pthread_t id1,id2;
    pthread_t mon_th_id;
    int ret;
    end_time = time(NULL)+10;
   /*互斥锁初始化*/
     pthread_mutex_init(&mutex,NULL);
   /*创建两个线程*/
     ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);
     if(ret!=0)
     perror("pthread cread1");
     ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
     if(ret!=0)
     perror("pthread cread2");
     pthread_join(id1,NULL);                                 
     pthread_join(id2,NULL);                                 
     exit(0);                                                
   }
                                                          
   void pthread1(void *arg)                                
   {                                                       
    int i;                                                  
    while(time(NULL) < end_time)
    {                           
    /*互斥锁上锁*/                                          
     if(pthread_mutex_lock(&mutex)!=0)
     {                      
      perror("pthread_mutex_lock");                           
     }                                                       
     else                                                    
     printf("pthread1:pthread1 lock the variable\n");        
     for(i=0;i<2;i++){                                       
     sleep(1);                                               
     lock_var++;                                             
     }                                                       
   /*互斥锁接锁*/                                          
     if(pthread_mutex_unlock(&mutex)!=0){                    
     perror("pthread_mutex_unlock");                         
     }                                                       
     else                                                    
     printf("pthread1:pthread1 unlock the variable\n");      
     sleep(1);                                               
    }                                                       
   }                                                       
   void pthread2(void *arg)                                
   {                                                       
    int nolock=0;                                           
    int ret;                                                
    while(time(NULL) < end_time)
    {                           
     /*测试互斥锁*/                                          
     ret=pthread_mutex_trylock(&mutex);                      
     if(ret==EBUSY)                                          
     printf("pthread2:the variable is locked by pthread1\n");
     else
     {   
      if(ret!=0)
      {                                                                                       
        perror("pthread_mutex_trylock");                  
        exit(1);                                          
       }                                                 
       else                                              
       printf("pthread2:pthread2 got lock.The variable is%d\n",lock_var);                                  
               /*互斥锁接锁*/                                    
       if(pthread_mutex_unlock(&mutex)!=0)
       {              
        perror("pthread_mutex_unlock");                   
       }                                                 
       else                                              
       printf("pthread2:pthread2 unlock the variable\n");
      }                                                 
     sleep(3);                                         
     }                                                 
    } 


 3.读写锁

1)多个读者可以同时进行读
2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)


int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);

初始化读/写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

锁定读/写锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

读取读/写锁上的锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

写读/写锁上的锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

解除读/写锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

成功返回0,否则返回错误编号

实例:

 

#include <stdlib.h>
#include <pthread.h>

struct job {
	struct job *j_next;
	struct job *j_prev;
	pthread_t   j_id;   /* tells which thread handles this job */
	/* ... more stuff here ... */
};

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

/*
 * Initialize a queue.
 */
int
queue_init(struct queue *qp)
{
	int err;

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

	/* ... continue initialization ... */

	return(0);
}

/*
 * Insert a job at the head of the queue.
 */
void
job_insert(struct queue *qp, struct job *jp)
{
	pthread_rwlock_wrlock(&qp->q_lock);
	jp->j_next = qp->q_head;
	jp->j_prev = NULL;
	if (qp->q_head != NULL)
		qp->q_head->j_prev = jp;
	else
		qp->q_tail = jp;	/* list was empty */
	qp->q_head = jp;
	pthread_rwlock_unlock(&qp->q_lock);
}

/*
 * Append a job on the tail of the queue.
 */
void
job_append(struct queue *qp, struct job *jp)
{
	pthread_rwlock_wrlock(&qp->q_lock);
	jp->j_next = NULL;
	jp->j_prev = qp->q_tail;
	if (qp->q_tail != NULL)
		qp->q_tail->j_next = jp;
	else
		qp->q_head = jp;	/* list was empty */
	qp->q_tail = jp;
	pthread_rwlock_unlock(&qp->q_lock);
}

/*
 * Remove the given job from a queue.
 */
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->q_tail = NULL;
	} else if (jp == qp->q_tail) {
		qp->q_tail = jp->j_prev;
		if (qp->q_head == jp)
			qp->q_head = NULL;
	} else {
		jp->j_prev->j_next = jp->j_next;
		jp->j_next->j_prev = jp->j_prev;
	}
	pthread_rwlock_unlock(&qp->q_lock);
}

/*
 * Find a job for the given thread ID.
 */
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 = qp->q_head; jp != NULL; jp = jp->j_next)
		if (pthread_equal(jp->j_id, id))
			break;

	pthread_rwlock_unlock(&qp->q_lock);
	return(jp);
}

 

 

这个例子中,不管什么时候需要增加一个作业也不能队列中或者从队列中删除作业,都用写模式锁住队列的读写锁。不管何时搜索队列,首先需要获取读模式下的锁,允许所有的工作线程并发地搜索队列。在这种情况下,只有线程搜索队列的频率远远高于增加或者删除作业时,使用读写锁才可能改善性能。



4.条件变量

 

条件变量是线程可用的另一种同步机制。通常和互斥量一起使用,允许线程以无竞争的方式等待特定条件发生。条件本身由互斥信号量保护,线程在改变条件状态之前必须首先锁住信号量,在获得信号量之前不会察觉到这种改变。

    条件变量在使用之前要初始化。动态分配的可以由pthread_cond_init函数来分配,静态变量可以直接将PTHREAD_COND_INITIALIZER赋给静态分配的条件。动态分配的变量需要使用pthread_mutex_destory来去初始化,然后再free。

静态创建:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
动态创建:pthread_cond _t cond;
  pthread_cond_init(&cond,NULL);

其中的第二个参数NULL表示条件变量的属性,虽然POSIX中定义了条件变量的属性,但在LinuxThread中并没有实现,因此常常忽略

int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                      pthread_mutex_t *restrict mutex,
                      const strcut timespec *restrict timeout);
/*若成功则返回真,错误则返回错误编号*/
传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的信号量传给函数,函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁。这个是原子操作,当它返回时,互斥量会被再次锁住。
pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数相似。只是多了一个timeout。timeout变量指定了等待的时间。时间的结构:
struct timespec{
time_t tv_sec; /*seconds*/
long tv_nsec; /*nanoseconds*/
}
int pthread_cond_signal(pthread_cond_t *cond); /*唤醒等待该条件的某个进程*/
int pthread_cond_broadcast(pthread_cond_t *cond); /*唤醒等待该条件的所有进程*/
要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。

 

实例:

 

#include<stdio.h>
#include<pthread.h>

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
int count=5;

void *decrement(void *arg)
{
while(1)
{
    pthread_mutex_lock(&mutex);
    while(count<=0)
    {
        printf("count<=0,thread1 is hanging!\n");
        pthread_cond_wait(&cond,&mutex);
        sleep(1);
        printf("sleep!\n");
    }
    count=count-1;
    pthread_mutex_unlock(&mutex);    

    if(count==9)
    {
        printf("count=9,thread1 is over!\n");
        return NULL;
    }
}

}

void *increment(void *arg)
{sleep(1);
while(1)
{
    pthread_mutex_lock(&mutex);
    count=count+1;
    if(count>0)
    {
        printf("count=%d,change cond state!\n",count);
        pthread_cond_signal(&cond);
    }
    pthread_mutex_unlock(&mutex);

    if(count==10)
    {
        printf("count=10,thread2 is over!\n");
        return NULL;
    }
}

}

int main()
{
    int i1=1,i2=1;
    pthread_t id1,id2;
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    pthread_create(&id1,NULL,decrement,NULL);
    pthread_create(&id2,NULL,increment,NULL);
    
    i2=pthread_join(id2,NULL);
    i1=pthread_join(id1,NULL);
    if((i2==0)&&(i1==0))
    {
        printf("count=%d,the main thread!\n",count);
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);
        return 0;
    }
}

 

分享到:
评论

相关推荐

    线程同步-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

    线程同步-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

    Linux学习笔记7-仿STM32自建寄存器库实验代码

    这个“Linux学习笔记7-仿STM32自建寄存器库实验代码”是为了帮助理解如何在Linux环境下模拟STM32的寄存器操作,以便在没有实际硬件的情况下进行软件开发和测试。 首先,STM32是意法半导体(STMicroelectronics)...

    多线程学习笔记

    多线程学习笔记 iOS开发中,多线程是一种常见的技术手段,用于优化应用程序的性能,提升用户体验。多线程的核心是让程序能够并发地执行多个任务,合理地利用设备的计算能力,尤其是在拥有多个核心的处理器上。 ...

    Linux 进程 线程学习笔记

    在深入探讨Linux下C语言编程中进程和线程的创建之前,我们先来理解一下进程与线程的基本概念。 - **进程**:是操作系统进行资源分配和调度的基本单位,每个进程都有独立的地址空间、内存和系统资源。在Linux环境下...

    Linux C 学习笔记

    以上只是部分学习笔记内容的解析,完整的笔记可能包含了更多关于文件I/O、进程管理、信号处理、线程同步等Linux C编程的核心概念。学习Linux C编程需要深入理解操作系统的工作原理,熟练掌握C语言基础,并熟悉Linux...

    linux编程学习笔记PDF资料下载.txt

    根据提供的文件信息,我们可以推断出这是一份关于Linux编程学习笔记的PDF资料。下面将对这份资料可能涉及的关键知识点进行详细的阐述。 ### Linux编程基础知识 #### 1. Linux操作系统概述 - **定义与特点**:Linux...

    Linux多线程编程_linux多线程_Linux多线程;应用笔记_columnc9g_

    4. **线程同步**:为了确保线程间的正确执行顺序,需要进行同步操作。这包括: - **互斥锁(Mutex)**:`pthread_mutex_init()`, `pthread_mutex_lock()`, `pthread_mutex_unlock()`用于保护共享资源,防止竞态条件...

    linux c书本学习以及自己的学习笔记

    环境C程序设计"的书籍资源,由徐诚编著,并且包含了作者的学习笔记,特别标注了重点内容和需要关注的部分,特别适合C语言的初学者作为入门材料。 C语言是一种强大的、低级的编程语言,广泛用于操作系统开发、嵌入式...

    computer_basics_study_notes:计算机基础学习笔记,包含计算机组成原理+操作系统+计算机网络

    计算机基础学习笔记,包含计算机组成原理+操作系统+计算机网络。 一、计算机组成原理 todo 二、操作系统 1. 进程管理 作业管理 储存管理 文件管理 Linux 文件系统 设备管理 2. - 线程同步 -- 互斥量 - 线程同步 -- ...

    Java学习笔记-个人整理的

    {1.13}Linux命令}{41}{section.1.13} {1.13.1}基本查看、移动}{41}{subsection.1.13.1} {1.13.2}权限}{42}{subsection.1.13.2} {1.13.3}打包备份与恢复}{42}{subsection.1.13.3} {1.13.3.1}\texttt {tar},\text...

    UNIX多线程学习笔记

    【UNIX多线程学习笔记】 在UNIX操作系统中,多线程是一种重要的编程模型,它允许多个执行流在单个进程中并发运行。多线程带来了许多优势,包括提高应用程序响应速度,充分利用多CPU系统的资源,以及优化程序结构,...

    linux内核设计与实现第二版 学习笔记

    在《Linux内核设计与实现》第二版的学习笔记中,我们可以深入探讨以下几个关键知识点: 1. **内核架构**:Linux内核采用微内核架构,主要由进程管理、内存管理、文件系统、设备驱动和网络协议栈等模块组成。这些...

    linux\牛人笔记

    C语言是Linux编程的主要语言,通过学习标准IO库、系统调用、信号处理、线程与进程同步等内容,可以开发高效且稳定的程序。 4. **嵌入式Linux**:嵌入式Linux将Linux操作系统应用到资源有限的设备中,如路由器、智能...

    linux系统编程笔记

    Linux系统编程笔记涉及到的内容广泛,涵盖了从基础的出错处理到进程管理,从内存管理到进程间通信,以及守护进程设计等多个层面的知识。下面详细说明各个部分的知识点: 1. 常见出错处理 - abort函数用于异常终止...

    Linux系统编程学习笔记

    ### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭由 `FILE *` 指向的文件...

    linux驱动开发学习笔记.pdf

    以上就是Linux驱动开发学习笔记中提及的各个知识点,详细讲解了Linux驱动开发的基础知识和核心概念。每个知识点都与具体的Linux内核机制和编程实践紧密结合,是学习Linux内核驱动开发人员的宝贵资料。

    经典的Linux Kernel 电子笔记

    这份“经典的Linux Kernel电子笔记”显然包含了深入解析Linux内核架构和功能的重要信息。以下是对其中可能包含的知识点的详细阐述: 1. **Linux内核结构**:Linux内核采用了微内核设计,主要由进程管理、内存管理、...

    Linux从入门到精通学习文档

    ##### 1.4 Linux学习技巧 - **实践为主**: - 多动手操作,通过实践加深理解。 - 使用虚拟机或云环境练习。 - **资源利用**: - 学习官方文档和技术论坛。 - 加入社区和参与开源项目。 - **持续跟进**: - 关注...

Global site tag (gtag.js) - Google Analytics