Linux多线程编程API及步骤

线程概述

进程是系统中程序执行和资源分配的基本单位。
每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销,进程在演化中出现了另一个概念——线程。它是一个进程内的基本调度单位,也可以称为轻量级进程。
线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述和信号处理。因此,大大减少了上下文切换的开销。

同进程一样,线程也将相关的变量值放在线程控制表内TCB。
一个进程可以有多个线程,也就是有多个线程控制表及堆栈寄存器,但却共享一个用户地址空间。
要注意的是,由于线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响,因此,多线程中的同步就是非常重要的问题了。
在多线程系统中,进程与线程的关系如表 8.1 所示。

在这里插入图片描述

Linux线程实现

1.线程创建和退出

(1)函数说明
线程创建:pthread_create。
线程退出:
(1)在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了,这也是线程退出一种方法。
(2)另一种退出线程的方法是使用函数 pthread_exit。由于 exit 的作用是使调用进程终止,往往一个进程包含多个线程,因此,在使用 exit 之后,该进程中的所有线程都终止了。因此,在线程中就可以使用 pthread_exit 来代替进程中的 exit。
线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。
pthread_join()函数。pthread_join 可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。

在这里插入图片描述
*如:pthread_create( &id3,NULL, (void )thread3,&tmp );

在这里插入图片描述
在这里插入图片描述
以下实例中创建了两个线程,其中第一个线程是在程序运行到中途时调用 pthread_exit函数退出,第二个线程正常运行退出。在主线程中收集这两个线程的退出信息,并释放资源。从这个实例中可以看出,这两个线程是并发运行的。
gcc -o th th.c -lpthread (编译的时候要加入线程库)

由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑到线程间资源访问的惟一性问题,这里主要介绍 POSIX 中线程同步的方法,主要有互斥锁和信号量的方式。

2.mutex 互斥锁线程控制 (1)函数说明

mutex是一种简单的加锁的方法来控制对共享资源的存取。
这个互斥锁只有两种状态,也就是上锁和解锁.
可以把互斥锁看作某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。可以说,这把互斥锁使得共享资源按序在各个线程中操作。

互斥锁初始化:pthread_mutex_init
互斥锁上锁:pthread_mutex_lock—阻塞
互斥锁判断上锁:pthread_mutex_trylock —非阻塞(如果已经上锁,则返回EBUSY;如果没上锁,则给它上锁,成功为0,不成功为其他值)
互斥锁解锁:pthread_mutex_unlock
消除互斥锁:pthread_mutex_destroy

互斥锁可以分为:
快速锁:指调用线程会阻塞直至拥有互斥锁的线程解锁为止。
递归互斥锁:能够成功地返回并且增加调用线程在互斥上加锁的次数.
检错互斥锁:则为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息。

在这里插入图片描述

在这里插入图片描述

3.信号量线程控制 (1)信号量说明

信号量也就是操作系统中所用到的 PV 原语,它广泛用于进程或线程间的同步与互斥,它本质上是一个非负的整数计数器。
PV 原语是对整数计数器信号量 sem 的操作。一次 P 操作使 sem 减一(分配资源),而一次 V 操作使sem 加一(释放资源)。
进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem 的值大于或等于0时,该进程(或线程)具有公共资源的访问权限;相反,当信号量 sem的值小于零时,该进程(或线程)就将阻塞直到信号量 sem 的值大于等于0 为止。
PV原语主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量 sem,它们的操作流程如图 8.2 所示。
当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,它们的操作流程如图 8.3 所示。

在这里插入图片描述
在这里插入图片描述

sem_init 用于创建一个信号量,并能初始化它的值。
sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于0时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。
sem_post 相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。
sem_getvalue 用于得到信号量的值。
sem_destroy 用于删除信号量。

在这里插入图片描述
在这里插入图片描述

信号量锁例子:

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

猜你喜欢

转载自blog.csdn.net/hhltaishuai/article/details/107719726