多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。详细请见下表:
在这里插入图片描述

线程创建API

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否则返回错误编号

当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。

新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

2. 线程退出

单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:

1)线程只是从启动例程中返回,返回值是线程的退出码。

2)线程可以被同一进程中的其他线程取消。

3)线程调用pthread_exit:

#include <pthread.h>
int pthread_exit(void *rval_ptr);
  rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。

3. 线程等待

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号

调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回i,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。

可以通过调用pthread_join自动把线程置于分离状态,这样资源就可以恢复。如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。

如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获得线程的终止状态。

4. 线程脱离

一个线程或者是可汇合(joinable,默认值),或者是脱离的(detached)。当一个可汇合的线程终止时,它的线程ID和退出状态将留存到另一个线程对它调用pthread_join。脱离的线程却像守护进程,当它们终止时,所有相关的资源都被释放,我们不能等待它们终止。如果一个线程需要知道另一线程什么时候终止,那就最好保持第二个线程的可汇合状态。

pthread_detach函数把指定的线程转变为脱离状态。

#include <pthread.h>
int pthread_detach(pthread_t thread);
// 返回:若成功返回0,否则返回错误编号
  本函数通常由想让自己脱离的线程使用,就如以下语句:

pthread_detach(pthread_self());

5. 线程ID获取及比较

#include <pthread.h>
pthread_t pthread_self(void);
// 返回:调用线程的ID
  对于线程ID比较,为了可移植操作,我们不能简单地把线程ID当作整数来处理,因为不同系统对线程ID的定义可能不一样。我们应该要用下边的函数:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
// 返回:若相等则返回非0值,否则返回0

对于多线程程序来说,我们往往需要对这些多线程进行同步。同步(synchronization)是指在一定的时间内只允许某一个线程访问某个资源。而在此时间内,不允许其它的线程访问该资源。我们可以通过互斥锁(mutex),条件变量(condition variable)和读写锁(reader-writer lock)来同步资源。在这里,我们暂不介绍读写锁。

与互斥锁有关API

互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。

在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。

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

1. 创建及销毁互斥锁

#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。  

2. 加锁及解锁

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

线程Mutex互斥锁例子

#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;
	
	int ret;

	end_time = time ( NULL ) +10;

	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 );
	pthread_mutex_destroy(&mutex);
	exit ( 0 );
}

void pthread1 ( void *arg )
{
	int i;
	while ( time ( NULL ) < end_time )//time()取得当前时间
	{
		if ( pthread_mutex_lock ( &mutex ) !=0 )
		{
			perror ( "pthread_mutex_lock" );
			
			pthread_exit((void *)-1);
			
		}
		else
			printf ( "pthread1 lock\n" );

		
		for ( i=0;i<2;i++ )
		{
			sleep (1 );
			lock_var++;
			printf("pthread1 lock_var=%d\n",lock_var);
		}

		
		if ( pthread_mutex_unlock ( &mutex ) !=0 )
		{
			perror ( "pthread_mutex_unlock" );
		}
		else
			printf ( "pthread1 unlock\n" );
		sleep ( 1 );
	}
}

void pthread2 ( void *arg )
{
	
	int ret;
	int i;
	while ( time ( NULL ) < end_time )
	{    
	     //*
	      if((ret= pthread_mutex_lock ( &mutex ))!=0) //阻塞
	      	{
	           perror("can't get lock\n");
		    	pthread_exit((void *)-1);
	      	}
	        
	      // */
	       /*
		ret=pthread_mutex_trylock ( &mutex );//非阻塞
		if ( ret==EBUSY )
			printf ( "pthread2:the variable is locked by pthread1\n" );
		//*/
		else if(ret==0)
		{		
			printf ( "pthread2 got lock\n");
			
			for ( i=0;i<2;i++ )
			{
				sleep (1);
				lock_var++;
				printf("pthread2 lock_var=%d\n",lock_var);	
				
			}
			
			if ( pthread_mutex_unlock ( &mutex ) !=0 )
			{
				perror ( "pthread_mutex_unlock" );
			}
			else
				printf ( "pthread2 unlock\n" );
		}
	sleep (1 );
	}
}

信号量互斥锁例子

//用信号量实现三个线程的互斥操作,且它们的执行顺序是线程1-》2--》3--》1--》2--》3.。。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>
int lock_var;
time_t end_time;
sem_t sem1,sem2,sem3;

void pthread1(void *arg);
void pthread2(void *arg);
void pthread3(void *arg);


int main(int argc, char *argv[])
{
	pthread_t id1,id2,id3;
	pthread_t mon_th_id;
	int ret;
	end_time = time(NULL)+30;
	ret=sem_init(&sem1,0,1);
	ret=sem_init(&sem2,0,0);
	ret=sem_init(&sem3,0,0);
	if(ret!=0)
	{
		perror("sem_init");
	}
	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");

	ret=pthread_create(&id3,NULL,(void *)pthread3, NULL);
	if(ret!=0)
		perror("pthread cread2");
	
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_join(id3,NULL);
	exit(0);
}

void pthread1(void *arg)
{
	int i;
	while(time(NULL) < end_time){
		sem_wait(&sem1);
		for(i=0;i<2;i++){
			sleep(1);
			lock_var++;
			printf("pthread1----lock_var=%d\n",lock_var);
		}
		printf("pthread1:lock_var=%d\n",lock_var);
		sem_post(&sem2);
		sleep(1);
	}
}

void pthread2(void *arg)
{
	int nolock=0;
	int ret;
	while(time(NULL) < end_time){
	sem_wait(&sem2);
	printf("pthread2:pthread2 got lock;lock_var=%d\n",lock_var);
	sem_post(&sem3);
	sleep(3);
	}
}

void pthread3(void *arg)
{
	int nolock=0;
	int ret;
	while(time(NULL) < end_time){
	sem_wait(&sem3);
	printf("pthread3:pthread3 got lock;lock_var=%d\n",lock_var);
	sem_post(&sem1);
	sleep(3);
	}
}

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐