我是靠谱客的博主 想人陪彩虹,最近开发中收集的这篇文章主要介绍linux下同步与互斥,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

1、互斥锁

1.1 普通互斥锁

1.2 递归锁

2、读写锁

3、自旋锁

4、条件变量

4.1 条件变量的原理和接口

4.2 虚假唤醒

4.3 broadcast的例子

5、信号量

5.1 信号量用于同步

5.2 信号量用于互斥

5.3 sem_timedwait的例子

6、pthread标准的其它函数

6.1 pthread_self (void) ;

6.2 pthread_exit (void * retval) ;

6.3 pthread_cance (pthread_t thread) ;

7、Windows与Linux接口对比

最后:linux带pthread编译:


锁主要有两个用途:

1 解决互斥访问资源

2 解决一个任务对另一个任务的依赖

 

关于linux内核:

linux内核只提供了轻量进程的支持,未实现线程模型。Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。

 

 

1、互斥锁

1.1 普通互斥锁

这里普通互斥锁指的是非递归互斥锁。

为了保护共享资源,使我们线程可以单独使用某个共享资源,使用之前先上锁,当其他进程要使用的时候,就需要等待到这个线程用完之后,再开锁。

  接口与定义:

  声明互斥锁:

            pthread_mutex_t  m; 

    初始化互斥锁: 

            int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

            第一个参数:就是我们前面声明的锁,因为这个参数需要传递的是一个锁的指针,所以需要有一个取地址符。

            第二个参数:是这个锁的属性,我们让它是默认的属性,这里设置为NULL

             返回值:成功返回0, 失败返回-1

    上锁:锁住某个资源

            int pthread_mutex_lock(pthread_mutex_t *mutex);        这个函数是阻塞型。

            int pthread_mutex_trylock(pthread_mutex_t *mutex);    这个是非阻塞型的。

        返回值:成功返回0,失败返回-1

            

    解锁:

            int pthread_mutex_unlock(pthread_mutex_t *mutex);    

        返回值:成功返回0,失败返回-1

  

    销毁互斥锁:

            int pthread_mutex_destroy(pthread_mutex_t *mutex);

             返回值:成功返回0,  失败返回-1

 

实例 启动两个线程对同一个全局数组写入:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


pthread_mutex_t mutex; //定义互斥量
char* buf[5]; //字符指针数组  全局变量
int pos; //用于指定上面数组的下标


void *task(void *p)
{
    //使用互斥量进行加锁
	pthread_mutex_lock(&mutex);


	buf[pos] = (char *)p;
	sleep(1);
	pos++;


    //使用互斥量进行解锁
	pthread_mutex_unlock(&mutex);
}




void *task2(void *p)
{
	//使用互斥量进行加锁
	pthread_mutex_lock(&mutex);


	buf[pos] = (char *)p;
	sleep(1);
	pos++;


	//使用互斥量进行解锁
	pthread_mutex_unlock(&mutex);
}




int main(void)
{
	//初始化互斥量, 默认属性
	pthread_mutex_init(&mutex, NULL);


	//启动一个线程 向数组中存储内容
	pthread_t tid, tid2;
	pthread_create(&tid, NULL, task, (void *)"zhangfei");
	pthread_create(&tid2, NULL, task2, (void *)"guanyu");


	//主线程进程等待,并且打印最终的结果
	pthread_join(tid, NULL);
	pthread_join(tid2, NULL);


    pthread_mutex_destroy(&mutex);
    
    int i = 0;
	printf("字符指针数组中的内容是:");
	for (i = 0; i < pos; ++i)
	{
		printf("%s ", buf[i]);
	}


  

 

输出:zhangfei guanyu

 

 

1.2 递归锁

递归锁也叫可重入锁(reentrant mutex),非递归锁也叫不可重入锁(non-reentrant mutex)。

同一个线程可以多次获取同一个递归锁,不会产生死锁。如果一个线程多次获取同一个非递归锁,则会产生死锁。

Windows下的Mutex和Critical Section是可递归的。 

Linux下的pthread_mutex_t锁是默认是非递归的。可以通过设置PTHREAD_MUTEX_RECURSIVE属性,将pthread_mutex_t锁设置为递归锁。

//create recursive attribute
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
 
//set recursive attribute
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 
pthread_mutex_init(&g_mutex, &attr);

 

可能使用到的场景:

//这两个函数不能同时运行,因为它们会修改类的内部,
//因此我想使用互斥锁。问题是,Process()在内部调用reset(),
//因为已经获取了mMutex,它将创建死锁。使用递归锁锁定它们可以解决此问题。
public void Process(...)
{
  acquire_mutex(mMutex);
  // Heavy processing
  ...
  reset();
  ...
  release_mutex(mMutex);
}

public void reset()
{
  acquire_mutex(mMutex);
  // Reset
  ...
  release_mutex(mMutex);
}

 

模拟实现:

class RecursiveMutex : protected Mutex
{
private:
	volatile int32 _currentThread;
	int32 _lockCount;
	...
}
	
void RecursiveMutex::Take() throw(Exception)
{
	if ( !IsThisThreadOwner() )
	{
		Mutex::Take();//只有当不是当前线程的时候才会执行pthread_mutex_lock
		MakeThisThreadOwner();
	}

	AtomicIncrement( _lockCount );
}

IsThisThreadOwner: 如果这个mutex的_currentThread是当前线程则返回true
不管是不是,都会把_currentThread变成当前线程
如果就是当前线程正在占有的该mutex,则只是计数+1

 

 

2、读写锁

读写锁也是用来控制互斥访问,特点是:

  • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作;
  • 如果有其它线程写数据,则其它线程都不允许读、写操作

所以读写锁的规则就是:

  • 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁;
  • 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。

读写锁适合于对资源的读次数比写次数多得多的情况。

读写锁的接口:

#include <pthread.h>

// 初始化读写锁

int pthread_rwlock_init(pthread_rwlock_t *rwlock,

const pthread_rwlockattr_t *attr);

 

// 申请读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );

 

// 申请写锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );

 

// 非阻塞的方式获取写锁

// 如果有任何的读者或写者持有该锁,则立即失败返回。

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

 

// 解锁

int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);

 

// 销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

 

实例,两个读线程两个写线程,写互斥地对字符数组操作:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


char cBuf[30];
pthread_rwlock_t rwlock; //读写锁


//write线程函数,负责往buf里面加字符
void *taskWrite(void *p)
{
	while (1)
	{
		pthread_rwlock_wrlock(&rwlock);
		if (pos < 29)
		{
			cBuf[pos] = 'A';
			pos++;
			printf("Write cBuf is: %s n", cBuf);
		}
		pthread_rwlock_unlock(&rwlock);
		sleep(1);
	}
}


//write线程函数,负责从buf里面删除字符
void *taskWriteDel(void *p)
{
	while (1)
	{
		pthread_rwlock_wrlock(&rwlock);
		if (pos > 0)
		{
			cBuf[pos-1] = 0;
			pos--;
			printf("WriteDel cBuf is: %s n", cBuf);
		}
		pthread_rwlock_unlock(&rwlock);
		sleep(1);
	}
}


void *taskRead(void *p)
{
	while (1)
	{
		pthread_rwlock_rdlock(&rwlock);
		if (pos > 0)
		{
			printf("read cBuf is: %s n", cBuf);
		}
		pthread_rwlock_unlock(&rwlock);
	}
}


void *taskRead2(void *p)
{
	while (1)
	{
		pthread_rwlock_rdlock(&rwlock);
		if (pos > 0)
		{
			printf("read2 cBuf is: %s n", cBuf);
		}
		pthread_rwlock_unlock(&rwlock);
	}
}


int main(void)
{
    pthread_t ptd1, ptd2, ptd3, ptd4;
	pos = 0;


	pthread_rwlock_init(&rwlock, NULL);//初始化一个读写锁  


	//创建线程  
	pthread_create(&ptd1, NULL, taskWrite, NULL);
	pthread_create(&ptd2, NULL, taskWriteDel, NULL);
	pthread_create(&ptd3, NULL, taskRead, NULL);
	pthread_create(&ptd4, NULL, taskRead2, NULL);


	//等待线程结束,回收其资源  
	pthread_join(ptd1, NULL);
	pthread_join(ptd2, NULL);
	pthread_join(ptd3, NULL);
	pthread_join(ptd4, NULL);


	pthread_rwlock_destroy(&rwlock);//销毁读写锁  
	

结果:cBuf中的字符数量时而增加时而减少,具体不打印了

 

 

3、自旋锁

自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。

接口上就是把互斥锁的mutex改成spin。

 

实例 两个线程各自数数:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


pthread_spinlock_t spinMutex;


void *task5(void *p)
{
	int cur = 0;
	while (1)
	{
		//3.使用互斥量进行加锁
		pthread_spin_lock(&spinMutex);
		printf("task5 %dn", cur);
		cur++;
		sleep(0);
		//4.使用互斥量进行解锁
		pthread_spin_unlock(&spinMutex);
	}
}
void *task6(void *p)
{
	int cur = 0;
	while (1)
	{
		//3.使用互斥量进行加锁
		pthread_spin_lock(&spinMutex);
		printf("task6  %dn", cur);
		cur++;
		sleep(0);
		//4.使用互斥量进行解锁
		pthread_spin_unlock(&spinMutex);
	}
}


int main(void)
{
    pthread_spin_init(&spinMutex, NULL);


    pthread_t tid3, tid4;
	pthread_create(&tid3, NULL, task5, (void *)"zhangfei");
	pthread_create(&tid4, NULL, task6, (void *)"zhangfei");


	pthread_join(tid3, NULL);
	pthread_join(tid4, NULL);


    pthread_spin_destroy(&spinMute

 

 

4、条件变量

 

4.1 条件变量的原理和接口

条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。线程在改变条件状态之前必须首先锁住互斥量

在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。阻塞时处于解锁状态。

 

接口:

#include <pthread.h>

// 初始化条件变量

int pthread_cond_init(pthread_cond_t *cond,

pthread_condattr_t *cond_attr);

 

// 阻塞等待

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

 

// 超时等待

int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,

const timespec *abstime);

 

// 解除所有线程的阻塞

int pthread_cond_destroy(pthread_cond_t *cond);

 

// 至少唤醒一个等待该条件的线程

int pthread_cond_signal(pthread_cond_t *cond);

 

// 唤醒等待该条件的所有线程

int pthread_cond_broadcast(pthread_cond_t *cond);

 

实例, 一个写线程两个读线程,读完了就删,写完了就给读线程发信号:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


int pos; //用于指定上面数组的下标
char cBuf[30];


//用作条件变量
pthread_cond_t tcond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t tmutex = PTHREAD_MUTEX_INITIALIZER;


void *taskCondWrite(void *p)
{
	while (1)
	{
		if (pos < 29)
		{
			cBuf[pos] = 'A';
			pos++;
			printf("Write cBuf is: %s n", cBuf);
			pthread_cond_signal(&tcond);
			sleep(1);
		}
	}
}




void *taskCondRead1(void *p)
{
	while (1)
	{
		pthread_mutex_lock(&tmutex);
		pthread_cond_wait(&tcond, &tmutex);
		pthread_mutex_unlock(&tmutex);
		if (pos > 0)
		{
			printf("read1 cBuf is: %s n", cBuf);
		}
		sleep(1);
	}
}




void *taskCondRead2(void *p)
{
	while (1)
	{
		pthread_mutex_lock(&tmutex);
		pthread_cond_wait(&tcond, &tmutex);
		pthread_mutex_unlock(&tmutex);
		if (pos > 0)
		{
			printf("read2 cBuf is: %s n", cBuf);
		}
		sleep(1);
	}
}


int main(void)
{
	//一个写线程两个读线程,写完了就给读线程发信号,看是什么效果
	pthread_t ptd1, ptd2, ptd3;
	pthread_create(&ptd1, NULL, taskCondWrite, NULL);
	pthread_create(&ptd2, NULL, taskCondRead1, NULL);
	pthread_create(&ptd3, NULL, taskCondRead2, NULL);




	//等待线程结束,回收其资源  
	pthread_join(ptd1, NULL);
	pthread_join(ptd2, NULL);
	pthread_join(ptd3, NULL);


	pthread_cond_destroy(&tmutex);
	
    

 

 

4.2 虚假唤醒

 

在多核处理器下,pthread_cond_signal可能会激活多于一个线程。结果是,当一个线程调用pthread_cond_signal()后,多个调用pthread_cond_wait()或pthread_cond_timedwait()的线程返回。这种效应成为”虚假唤醒”(spurious wakeup)。

通常的解决方法是将if改为while:

mj中的实现:

void ArcConditionVariable::Wait(void) throw(ArcException)
{
status = pthread_mutex_lock(&_lock);
while (_pred == 0) //signal和broadcast的时候设置成1,这个是用来防止虚假唤醒
{
status = pthread_cond_wait(&_cond, &_lock);
}
_pred = 0; //最后加一个这个,还能保证,在broadcast的时候,都能往下进行么?
status = pthread_mutex_unlock(&_lock);
}

 

参考:https://www.jianshu.com/p/0eff666a4875

 

 

4.3 broadcast的例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


//用作条件变量
pthread_cond_t tcond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t tmutex = PTHREAD_MUTEX_INITIALIZER;


int pred = 0;




void *thread1_entry(void *arg)
{
	while (1)
	{
		pthread_mutex_lock(&tmutex);
		printf("cond wait tcond1n");
		
		while (pred == 0)
		{
			pthread_cond_wait(&tcond, &tmutex);
			printf("recv cond1n");
		}
		pred = 0;


		pthread_mutex_unlock(&tmutex);
	}
}


void *thread2_entry(void *arg)
{
	while (1)
	{
		pthread_mutex_lock(&tmutex);
		printf("cond wait tcond2n");


		while (pred == 0)//防止虚假唤醒
		{
			pthread_cond_wait(&tcond, &tmutex);
			printf("recv cond2n");
		}
		pred = 0;


		pthread_mutex_unlock(&tmutex);
	}
}


void *thread3_entry(void *arg)
{
	int ret;
	while (1)
	{
		pthread_mutex_lock(&tmutex);


		ret = pthread_cond_broadcast(&tcond);
		if (ret < 0)
		{
			printf("pthread_cond_broadcast errorn");
		}
		else
		{ 
			printf("pthread_cond_broadcast !n");
		}
		pred = 1;


		pthread_mutex_unlock(&tmutex);
		sleep(2);
	}
}


int main(void)
{
    //展示:thread3 通过broadcast唤醒thread1 和 thread2 各一次
	pthread_t ptd1, ptd2, ptd3;
	pthread_create(&ptd1, NULL, thread1_entry, NULL);
	pthread_create(&ptd2, NULL, thread2_entry, NULL);
	pthread_create(&ptd3, NULL, thread3_entry, NULL);


	//等待线程结束,回收其资源  
	pthread_join(ptd1, NULL);
	pthread_join(ptd2, NULL);
	pthread_join(ptd3, NULL);


  

结果:

 

从结果可以看出在pred = 0之后同时被放行的线程不会执行

while (pred == 0)//防止虚假唤醒

这个循环体外面的内容。

所以要把需要执行的内容都放在这个循环体内,才能保证都执行。

 

 

 

5、信号量

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

与互斥锁在作用域上的区别:信号量: 进程间或线程间,互斥锁: 线程间。

编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

 

接口:

#include <semaphore.h>

// 初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);

 

// 信号量 P 操作(减 1)

int sem_wait(sem_t *sem);

 

// 信号量P操作,限时等待(返回之前还是减一了的)

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

 

// 以非阻塞的方式来对信号量进行减 1 操作

int sem_trywait(sem_t *sem);

 

// 信号量 V 操作(加 1)

int sem_post(sem_t *sem);

 

// 获取信号量的值

int sem_getvalue(sem_t *sem, int *sval);

 

// 销毁信号量

int sem_destroy(sem_t *sem);

 

信号量可以用于同步和互斥:

 

5.1 信号量用于同步

用于同步的例子, 每写两个字符就可以读一次,并且每读一次对sem_p进行两次V操作

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>


int pos; //用于指定上面数组的下标
char cBuf[30];


sem_t sem_g, sem_p; //定义两个信号量


void *pthread_g(void *arg)  //此线程改变字符ch的值
{
	while (1)
	{
		sem_wait(&sem_g);
		if (pos < 29)
		{
			cBuf[pos] = 'A';
			pos++;
			printf("Write cBuf is: %s n", cBuf);
			sleep(1);
		}
		sem_post(&sem_p);
	}
}


void *pthread_p(void *arg)  //此线程打印ch的值
{
	while (1)
	{
		sem_wait(&sem_p);
		sem_wait(&sem_p);


		if (pos > 0) //pthread_g要执行两次,才能走到这里
		{
			printf("pthread_p cBuf is: %s n", cBuf);
		}
		sleep(1);


		sem_post(&sem_g);
		sem_post(&sem_g);
	}
}


int main(void)
{
    pthread_t tid1, tid2;
	sem_init(&sem_g, 0, 0); // 初始化信号量为0
	sem_init(&sem_p, 0, 2); // 初始化信号量为2


	// 创建两个线程
	pthread_create(&tid1, NULL, pthread_g, NULL);
	pthread_create(&tid2, NULL, pthread_p, NULL);


	// 回收线程
	pthread_join(tid1, NULL);
	pthread_join(tid2, 

结果:

 

信号量用于同步的示意图:

 

 

5.2 信号量用于互斥

信号量用于互斥只需要一个信号量

// 信号量用于互斥实例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
 
sem_t sem; //信号量
 
void printer(char *str)
{
    sem_wait(&sem);//减一,p操作
    while(*str) // 输出字符串(如果不用互斥,此处可能会被其他线程入侵)
    {
        putchar(*str);  
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("n");
    
    sem_post(&sem);//加一,v操作
}
 
void *thread_fun1(void *arg)
{
    char *str1 = "hello";
    printer(str1);
}
 
void *thread_fun2(void *arg)
{
    char *str2 = "world";
    printer(str2);
}
 
int main(void)
{
    pthread_t tid1, tid2;
    
    sem_init(&sem, 0, 1); //初始化信号量,初始值为 1
    
    //创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);
    
    //等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL); 
    
    sem_destroy(&sem); //销毁信号量
    
    return 0;
}


信号量用于互斥的示意图:

 

 

5.3 sem_timedwait的例子

第一个参数表示多少秒之后会发出一个SIGALRM,第二个参数表示多长时间会超时

 

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>


sem_t sem;


#define handle_error(msg) 
	do { perror(msg); exit(EXIT_FAILURE); } while (0)


static void handler(int sig)
{
	write(STDOUT_FILENO, "sem_post() from handlern", 24);
	if (sem_post(&sem) == -1) {
		write(STDERR_FILENO, "sem_post() failedn", 18);
		_exit(EXIT_FAILURE);
	}
}


int main(int argc, char *argv[])
{
	struct sigaction sa;
	struct timespec ts;
	int s;


	if (argc != 3) {
		fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>n",
			argv[0]);
		exit(EXIT_FAILURE);
	}


	if (sem_init(&sem, 0, 0) == -1)
		handle_error("sem_init");


	/* Establish SIGALRM handler; set alarm timer using argv[1] */
	sa.sa_handler = handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	if (sigaction(SIGALRM, &sa, NULL) == -1)
		handle_error("sigaction");


	alarm(atoi(argv[1]));


	/* Calculate relative interval as current time plus
	number of seconds given argv[2] */
	if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
		handle_error("clock_gettime");


	ts.tv_sec += atoi(argv[2]);


	printf("main() about to call sem_timedwait()n");
	while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)
		continue;       /* Restart if interrupted by handler */


	/* Check what happened */
	if (s == -1) {
		if (errno == ETIMEDOUT)
			printf("sem_timedwait() timed outn");
		else
			perror("sem_timedwait");
	} else
		printf("sem_timedwait() succeededn");


	exit((s == 0) ? EXIT_SUCCESS : EXIT_

结果:

 $ ./a.out 2 3
           About to call sem_timedwait()
           sem_post() from handler
           sem_timedwait() succeeded
           $ ./a.out 2 1
           About to call sem_timedwait()
           sem_timedwait() timed out

 

 

 

6、pthread标准的其它函数

 

6.1 pthread_self (void) ;

调用获取自己的线程ID: 

 

6.2 pthread_exit (void * retval) ;

显式地退出线程

 

6.3 pthread_cance (pthread_t thread) ;

被其他线程用pthread_cance函数终止

 

 

 

7、Windows与Linux接口对比

事项

WIN32

Linux

线程创建

CreateThread

pthread_create

线程终止

执行完成后退出;线程自身调用ExitThread函数即终止自己;被其他线程调用函数TerminateThread函数

执行完成后退出;由线程本身调用pthread_exit 退出;被其他线程调用函数pthread_cance终止

获取线程ID

GetCurrentThreadId

pthread_self

创建互斥

CreateMutex

pthread_mutex_init

获取互斥

WaitForSingleObject、WaitForMultipleObjects

pthread_mutex_lock

释放互斥

ReleaseMutex

phtread_mutex_unlock

创建信号量

CreateSemaphore

sem_init

等待信号量

WaitForSingleObject

sem_wait

释放信号量

ReleaseSemaphore

sem_post

 

7.1 Windows同步对象Event

Event通过函数CreateMutex创建,可以分为手动模式和自动模式两种模式。

 

 

自动模式

手动模式

SetEvent

放过一个等待线程,

将Event调为激发态,放过一个等待线程,而后自动调回非激发态

一直放过等待

将Event调为激发态,

ResetEvent

无用

停止放过线程

将Event调为非激发态

PulseEvent

一次放过一个等待的线程,在这种模式下等同与SetEvent

放过所有的等待的线程一次,

将Event调为激发态,放过所有的等待的线程一次,然后调回非激发态

WaitForSingleObject(HANDLE,DOWRD)阻塞等待event变为有信号。

等待的可以是EVENT,也可以是其它内核对象。

如果线程等待的对象变为已通知状态,那么返回值是WAIT_OBJECT_0。

 

PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的. 

对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它释放所有等待的thread.  

也就是说在自动重置模式下PulseEvent和SetEvent的作用没有什么区别,但在手动模式下PulseEvent就有明显的不同,可以比较容易的控制程序是单步走,还是连续走。如果让循环按要求执行一次就用PulseEvent,如果想让循环连续不停的运转就用SetEvent,在要求停止的地方发ResetEvent。

 

Windows的Event与Linux的条件变量功能上是类似的,具体来说:

自动模式Event的SetEvent很像条件变量的pthread_cond_signal,手动模式的Event的PulseEvent很像广播pthread_cond_broadcast

 

跨平台例子:

ArcConditionVariable::ArcConditionVariable(const bool pBlockInitial) throw(ArcException)
{
#ifdef POSIX
	if(pBlockInitial)
		_pred = 0;	
	else _pred=1;
	int status = pthread_cond_init(&_cond, 0);

	pthread_mutexattr_t mutexattr;
	pthread_mutexattr_init(&mutexattr);

	status = pthread_mutex_init(&_lock, &mutexattr);
#endif

#ifdef WIN32
	if(pBlockInitial)
		_event = CreateEvent(NULL, FALSE, FALSE, NULL);
	else _event = CreateEvent(NULL, FALSE, TRUE, NULL);
#endif
}

ArcConditionVariable::~ArcConditionVariable() throw(ArcException)
{
#ifdef POSIX
	int status = pthread_cond_destroy(&_cond);
	status = pthread_mutex_destroy(&_lock);
#endif

#ifdef WIN32
	CloseHandle(_event);
#endif
}


void ArcConditionVariable::Wait(const float64 pDeltaT) throw(ArcException)
{
#ifdef POSIX
	
	int status;
	
	struct timespec timeout;
	timeout.tv_sec=time(0)+(int32)pDeltaT;
	timeout.tv_nsec=(int32)((pDeltaT-(int32)pDeltaT)*1.0e9);
	
	status = pthread_mutex_lock(&_lock);
	while (_pred == 0)
	{
		status = pthread_cond_timedwait(&_cond, &_lock, &timeout);
		if(status==ETIMEDOUT)
			break;
	}
	
	_pred = 0;
	status = pthread_mutex_unlock(&_lock);
#endif

#ifdef WIN32
	DWORD millis=(DWORD)(pDeltaT*1000.0);
	WaitForSingleObject(_event, millis);
#endif
}

void ArcConditionVariable::Wait(void) throw(ArcException)
{
#ifdef POSIX
	int status;
	
	status = pthread_mutex_lock(&_lock);
	while (_pred == 0)
	{
		status = pthread_cond_wait(&_cond, &_lock);
	}

	_pred = 0;
	status = pthread_mutex_unlock(&_lock);
#endif

#ifdef WIN32
	WaitForSingleObject(_event, INFINITE);
#endif
}

void ArcConditionVariable::Signal(void) throw(ArcException)
{
#ifdef POSIX
	int status;

	status = pthread_mutex_lock(&_lock);
	_pred = 1;
	status = pthread_cond_signal(&_cond);
	status = pthread_mutex_unlock(&_lock);
#endif

#ifdef WIN32
	SetEvent(_event);
#endif

}

void ArcConditionVariable::Reset(void) throw(ArcException)
{
#ifdef POSIX
	int status;

	status = pthread_mutex_lock(&_lock);
	_pred = 0;
	status = pthread_mutex_unlock(&_lock);
#endif

#ifdef WIN32
	ResetEvent(_event);
#endif
}

#ifdef POSIX
void ArcConditionVariable::Broadcast(void) throw(ArcException) 
{
	int status;
	
	status = pthread_mutex_lock(&_lock);
	_pred = 1;		
	status = pthread_cond_broadcast(&_cond);
	status = pthread_mutex_unlock(&_lock);
}
#endif

为什么Broadcast需要linux下面单独实现?

因为代码里面Event用的是自动模式,否则SetEvent与signal会是不一样的。

那么自动模式下的PulseEvent与broadcast又不是一个意思了。

 

ps:

HANDLE CreateEvent(   
    LPSECURITY_ATTRIBUTES lpEventAttributes,  // 安全属性
    BOOL bManualReset,               // 复位方式  
    BOOL bInitialState,               // 初始状态   
    LPCTSTR lpName                 // 对象名称   
);  

bManualReset:   

[输入]指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。   

 

 

 

最后:linux带pthread编译:

g++ -lpthread mytestthread.cpp -o testt

最后

以上就是想人陪彩虹为你收集整理的linux下同步与互斥的全部内容,希望文章能够帮你解决linux下同步与互斥所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部