Linux 线程

本文介绍了Linux线程的基础概念,包括线程与进程的区别、线程的创建与销毁,以及多线程同步和互斥的实现,如信号量、互斥锁和条件变量。重点讨论了NPTL线程库和POSIX线程API的使用。
摘要由CSDN通过智能技术生成

Linux 线程

线程基础

每个用户进程有自己的地址空间

系统为每个用户进程创建一个 task_struck 来描述该进程

  • 该结构体中包含了一个指针指向该进程的虚拟地址空间映射表

实际上 task_struct 和地址空间映射表一起用来表示一个进程

由于进程的地址空间是私有的,因此在进程间上下切换时,系统开销比较大

为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程

在同一个进程中创建的线程共享该进程的地址空间,并且和进程一样都参与内核同一的调度

Linux 里同样用 task_struct 来描述一个线程。线程和进程都参与统一的调度

进程和线程:

  • 线程是参与内核调度最小基本单位,进程是拥有资源的最小基本单位
  • 进程间相互独立,而同一个进程内的线程间共享进程内所有的资源
  • 多线程间通信简单,但是需要对临界资源进行互斥与同步操作,多进程间通信较难
  • 多线程安全性差,因为其中一个线程崩溃可能会对其他线程造成影响,多进程间相互独立,安全性高。

通常线程指的是共享相同地址空间的多个任务

使用多线程的好处

  • 大大提高了任务切换的效率
  • 避免了额外的 TLB & cache 的刷新

多线程通过第三方的线程库来实现

New POSIX Thread Library(NPTL)

  • 是早期 Linux Threads 的改进
  • 采用 1:1 的线程模型
  • 显著提高了运行效率
  • 信号处理效率更高

一个进程中的多个线程共享以下资源

  • 可执行的指令
  • 静态数据
  • 进程中打开的文件描述符
  • 信号处理函数
  • 当前工作目录
  • 用户 ID
  • 用户组 ID

每个线程的私有资源如下

  • 线程 ID(TID)
  • PC(程序计数器)和相关寄存器
  • 堆栈
    • 局部变量
    • 返回地址
  • 错误号(errno
  • 信号掩码和优先级
  • 执行状态和属性

多线程编程

NPTL 线程库中提供了如下基本操作

  • 创建线程
  • 删除线程
  • 控制线程

线程间同步和互斥机制

  • 信号量
  • 互斥锁
  • 条件变量

线程相关函数是由第三方库支持,所以编译时需要加上 -lpthread 选项

create a new thread - pthread_create

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

void *thread_func(void *arg){
	//printf("im %lu\n", pthread_self());
	pthread_exit(NULL);}

int main(int argc, const char *argv[]){
	pthread_t tid;
	int i = 0;
	while(1){
		if(pthread_create(&tid, NULL, thread_func, NULL) != 0){
			perror("fail to pthread_create");
			exit(1);}
		printf("tid : %d\n", ++i);
		pthread_join(tid, NULL);}
	while(1);
return 0;}
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

void *thread_func(void *arg){
	printf("i:%d\n", *(int *)arg);
	static int a = 200;
	pthread_exit(&a);}

int main(int argc, const char *argv[]){
	pthread_t tid;
	int i = 100;
	if(pthread_create(&tid, NULL, thread_func, &i) != 0){
		perror("fail to pthread_create");
		exit(1);}
	int *p;
	pthread_join(tid,(void **)&p);
	printf("a : %d\n", *p);
	while(1);
          
	return 0;}

terminate calling thread - pthread_exit

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

char message[32] = "Hello World";
void *thread_function(void *arg);

int main(int argc, const char *argv[]){
	pthread_t a_thread;
	void *thread_result;
	
	// 使用缺省属性创建线程
	if(pthread_create(&a_thread, NULL, thread_function, (void *)message) < 0){
		perror("fail to pthread_create");
		exit(-1);}
	printf("waiting for thread tp finish\n");
	
	// 等待线程结束
	if(pthread_join(a_thread, &thread_result) < 0){
		perror("fail to pthread_join");
		exit(-1);}
	printf("thread_result is %s\n", (char *)thread_result);
	printf("MESSAGE is now %s\n", message);
	
	return 0;}

void *thread_function(void *arg){
	printf("thread_function is running, argument is %s\n", (char *)arg);
	strcpy(message, "marked by thread");
	pthread_exit("Thank you for the cpu time");}
$ gcc ./sample.c -lpthread
$ ./a.out 
waiting for thread tp finish
thread_function is running, argument is Hello World
thread_result is Thank you for the cpu time
MESSAGE is now marked by thread

Linux 线程同步和互斥

线程间机制

  • 多线程共享同一个进程的地址空间
  • 优点:线程间很容易进行通信
    • 通过全局变量实现数据共享和交换
  • 缺点:多个线程同时访问共享对象时需要引入同步和互斥机制

信号量

线程间同步

同步(synchronization)指的是多个任务(线程)按照约定的顺序相互组合完成一件事情

1968年,Edsgar Dijkstra 基于信号量的概念提出了一种同步机制

由信号量来决定线程时继续运行还是阻塞等待

线程间同步 - P/V 操作

信号量代表某一类资源,其值表示系统中该资源的数量

信号量是一个受保护的变量,只能通过三种操作来访问

  • 初始化
  • P 操作(申请资源)
  • V 操作(释放资源)

信号量的值为非负整数

P(S)含义如下

  • if (信号量的值大于0){
    	申请资源的任务继续运行;
    	信号量的值减一;}
    else{申请资源的任务阻塞;}
    

V(S)含义如下

  • if(没有任务在等待该资源){信号量的值加一;}
    else{唤醒第一个等待的任务,让其继续运行}
    

Posix Semaphore API

posix 中定义了两类信号量

  • 无名信号量(基于内存的信号量)
  • 有名信号量

Pthread 库常用的信号量操作函数

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

线程同步示例

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

char buf[60];
sem_t sem;

void *function(void *arg);

int main(){
	pthread_t a_thread;
	void *thread_result;
	
	if(sem_init(&sem, 0, 0) < 0){
		perror("fail to sem_init\n");
		exit(-1);}
	
	if(pthread_create(&a_thread, NULL, function, NULL) < 0){
		perror("fail to pthread_create\n");
		exit(-1);}
	
	printf("input 'quit' to exit\n");
	do{
		fgets(buf, 60, stdin);
		sem_post(&sem);
	}while(strncmp(buf, "quit", 4) != 0);
	
	return 0;}

void *function(void *arg){
	while(1){
		sem_wait(&sem);
		printf("You enter %d characters\n", strlen(buf)-1);
    }
}

互斥锁

线程间互斥

  • 引入互斥(mutual exclusion)锁的目的是用来保证共享数据操作的完整性。
  • 互斥锁主要用来保护临界资源
  • 每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源
  • 线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止

Posix Mutex API

  • destroy and initialize a mutex - pthread_mutex_init
  • lock a mutex - pthread_mutex_lock
  • unlock a mutex - pthread_mutex_unlock
  • destroy and initialize a mutex - pthread_mutex_destroy
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>

unsigned int value1, value2, count;
pthread_mutex_t mutex;

void *function(void *arg);

int main(int argc, char *argv[]){
	pthread_t a_thread;
	
	if(pthread_mutex_init(&mutex, NULL) < 0){
		perror("fail to mutex_init");
		exit(-1);}
	
	if(pthread_create(&a_thread, NULL, function, NULL) < 0){
		perror("fail to pthread_create");
		exit(-1);}
	
	while(1){
		count++;
#ifdef _LOCK_
		pthread_mutex_lock(&mutex);
#endif
		value1 = count;
		value2 = count;
#ifdef _LOCK_
		pthread_mutex_unlock(&mutex);
#endif
	}
	return 0;}

void *function(void *arg){
	while(1){
#ifdef _LOCK_
		pthread_mutex_lock(&mutex);
#endif
		if(value1 != value2){
			printf("count = %d, value1 = %d, value2 = %d\n", count, value1, value2);
			usleep(100000);}
#ifdef _LOCK_
		pthread_mutex_unlock(&mutex);
#endif
	}
    
	return NULL;}

条件变量

  • int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
  • int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值