Linux系统编程——线程控制(一)

版权声明:版权归博主所有,转载请注明 https://blog.csdn.net/qq_17808707/article/details/88234396

一 线程的基本概念

线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述符和信号处理。操作系统在两个进程间进行切换时,要对前一个进程进行保护现场操作,对后一个进程进行还原现场操作。反复进行上下文切换会带来极大的开销。线程则无需进行上下文切换,因为多个线程共享同一个进程的上下文。多个线程也共享同一个进程的CPU时钟周期,进程的状态并未因线程切换而改变。

二线程的实现

1.用户态线程

用户态线程由进程负责调度管理、高度抽象化的、与硬件平台无关的线程机制。其最为显著的标志是,进程在创建多个线程时不需要LInux内核支持,也不直接对CPU标志寄存器进行操作。用户态的优势在于下面两方面:

  1. 减少多线程的系统开销:同一个进程可创建的线程数没有限制
  2. 用户态实现方式灵活:可根据实际需要设计相应的用户态线程机制,对于实时性要求高的程序格外重要

但是,如果某进程的其中一个线程被阻塞,则进程会进入睡眠态,其他线程同时也被阻塞。造成该现象的原因是Linux内核使用异步输入输出机制
用户态的缺陷是,无法发挥多路处理器和多核处理器的优势

2.内核态线程

由Linux操作系统根据CPU硬件的特点,以硬件底层模式实现的线程机制。内核态将所有线程按照同一调度算法调度,更有利于发挥多路处理器和多核处理器所支持的并发处理特性。
内核态线程可自有访问内存空间,并且在某一线程阻塞时,其他线程还能正常运行。
相对于用户态线程,内核态线程的系统开销稍大,并且必须通过系统调用实现,对硬件和Linux内核版本的依赖性较高,不利于程序移植。

三 POSIX线程库

libpthread线程库是目前Linux系统上最常用的线程库。它支持NPTL线程模式,以用户态线程实现。该函数库的接口被定义在pthread.h头文件中。

1.创建线程

int pthread_create(pthread_t* thread,pthread_attr_t* attr,void* (*start_routine)(void*),void* arg);

参数说明:

  • *thread:一个pthread_t结构的指针,该结构用于保存线程信息,函数创建成功时,将线程的标识符等信息写入*thread指针指向的内存空间
  • *attr:一个pthread_attr_t结构指针,结构中的元素分别对应着新线程的运行属性 ,属性的定义如下表,在没有特殊的属性要求时,可将NULL作为参数传递。
  • (*start_routine)(void*):表示需要传递的是start_routine()函数的地址,该函数以一个指向void的指针为参数,返回的也是指向void的指针,这种方式使任何类型的数据都能作为参数,也能返回给任何类型的数据结构。start_routine()函数的作用是启动线程
  • arg:start_routine()函数的参数

在这里插入图片描述
创建线程成功时返回0,失败时返回一个错误代码。需要注意的是,与Pthread相关的函数在错误时都不会返回-1。

2.结束线程

线程结束时可调用函数pthread_exit(),该函数的原理与结束进程的exit系统调用相似。它的作用是结束调用了这个函数的线程,返回一个指向某个变量的指针。这个指针绝对不能是局部变量的指针,因为局部变量会在线程出现严重问题时消失。函数的一般形式为:

void pthread_exit(void* retval);

如果进程需要在线程结束后与其归并到一起,可使用函数pthread_join()实现,该函数的原理与进程同步的wait系统调用相似。pthread_join()函数的一般形式如下:

int pthread_join(pthread_t th,void** thread_return);

参数说明:

  • th:指定要等待的线程,即是pthread_create()定义的标识符
  • thread_return:一个指针,指向另一个指针,后者指向线程的返回值

当等待的线程成功结束时,返回0,否则返回一个错误代码。
下例将演示从创建线程和结束线程的操作方法,新线程与原有线程共享变量:

#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<pthread.h>
#include<string>

using namespace std;

void* thread_function(void* arg);
string message="THREAD_TEST";
string* m=&message;

int main()
{
	int res;
	pthread_t a_thread;
	void* thread_result;
	res=pthread_create(&a_thread,NULL,thread_function,(void*)m);
	if(res!=0)
	{
		cerr<<"线程创建失败"<<endl;
		exit(EXIT_FAILURE);
	}
	cout<<"等待线程结束..."<<endl;
	res=pthread_join(a_thread,&thread_result);
	if(res!=0)
	{
		cerr<<"等待线程结束失败"<<endl;
		exit(EXIT_FAILURE);
	}
	cout<<"线程已结束,返回值:"<<(char*)thread_result<<endl;
	cout<<"Message的值为:"<<message<<endl;
	exit(EXIT_SUCCESS);
	return 0;
}

void* thread_function(void* arg)
{
	cout<<"线程在运行,参数为:"<<*((string*)arg)<<endl;
	sleep(3);
	*((string*)arg)="线程修改";
	char* retval= "线程执行完毕";
	pthread_exit(static_cast<void*>(retval));
} 

运行结果如下:
在这里插入图片描述
说明:如果线程创建成功,那么就会有两个线程在同时执行。原有线程将继续执行pthread_create()后的代码,新线程执行线程函数体内的代码。在验证新线程启动成功后,原有线程调用pthread_join()函数等待新线程结束。
新线程执行完毕后,pthread_join()函数将控制权还给主函数,主函数输出结束信息并退出。

四 同步

1.用信号量进行同步

与信号量相关的函数名字都以sem_作为前缀,线程里使用的基本信号量函数有4个,被包含在头文件semaphore.h中。

(1)初始化信号量:
int sem_init(sem_t* sem,int pshared,unsigned int value);

参数说明:

  • sem:sem_t结构指针,该结构用于保存信号量的信息
  • phshared:控制信号量的类型。如果参数为0,表明信号量是局部的,否则其他程序就能共享这个信号量
  • value:信号量初值
(2) 修改信号量
int sem_wait(sem_t* sem);
int sem_post(sem_t* sem);

这两个函数都是原子操作。sem_wait()函数的作用是使信号量减1,如果信号量的值为0(表明无可用资源,有其他线程正在访问),则sem_wait()函数会保留控制权,等待信号量变为非0后进行操作,再将控制权还给调用者。
sem_post()的作用是使信号量加1。

(3)信号量清理
int sem_destory(sem_t* sem);

以信号量指针作为参数,归还信号量所占用的资源,如果还有其他线程使用已清理的信号量,那么线程会收到一个错误。
下例将演示信号量操作方法:

#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<string>
#include<pthread.h>
#include<semaphore.h>

using namespace std;

void* thread_function(void* arg);
sem_t bin_sem;
const int WORK_SIZE=1024;
string work_area;

int main()
{
	int res;
	pthread_t a_thread;
	void* thread_result;
	res=sem_init(&bin_sem,0,0);
	if(res!=0)
	{
		cerr<<"初始化信号量失败"<<endl;
		exit(EXIT_FAILURE);
	}
	res=pthread_create(&a_thread,NULL,thread_function,NULL);
	if(res!=0)
	{
		cerr<<"线程创建失败"<<endl;
		exit(EXIT_FAILURE);
	}
	while(work_area!="end")
	{
		cout<<"请输入要传送的信息,输入'end'退出"<<endl;
		getline(cin,work_area);
		cout<<"输入内容为:"<<work_area<<"\n";
		sem_post(&bin_sem);
		sleep(1);
	}
	cout<<"\n等待线程结束"<<endl;
	res=pthread_join(a_thread,&thread_result); //等待线程结束
	if(res!=0)
	{
		cerr<<"线程结束失败"<<endl;
		exit(EXIT_FAILURE);
	}
	cout<<"线程结束"<<endl;
	sem_destroy(&bin_sem);
	exit(EXIT_SUCCESS);
}

void* thread_function(void* arg)
{
	sem_wait(&bin_sem);
	while(work_area!="end")
	{
		cout<<"收到内容:";
		cout<<work_area<<endl;
		sem_wait(&bin_sem);
	}
	pthread_exit(NULL);
}

输出结果如下:
在这里插入图片描述
线程被创建,两个线程属于同时运行,如果主线程未休眠,则会照成两个线程共用终端,故将主线程设置休眠。

猜你喜欢

转载自blog.csdn.net/qq_17808707/article/details/88234396
今日推荐