linux多线程

多线程概念:
进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作。为了进一步减少处理器的空转时间支持多处理器和减少上下文切换开销,也就出现了线程。(就是防止进程某一部分等待,导致进程白白切换)
在这里插入图片描述


线程的分类:
1)用户级线程:主要解决上下文切换问题,调度算法和调度过程全部由用户决定,在运行时不需要特定的内核支持。
缺点是无法发挥多处理器的优势

2)核心级线程:允许不同进程中的线程按照同一相对优先调度方法调度,发挥多处理器的并发优势
现在大多数系统都采用用户级线程和核心级线程并存的方法。一个用户级线程可以对应一个或多个核心级线程,也就是“一对一”或“一对多”模型。

线程创建的 Linux 实现
Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第 3 方 posix标准的 pthread,具有良好的可移植性。 编译的时候要在后面加上 –lpthread
---------------------------------------创建 -------------------------- 退出----------------------------等待
多进程 ----------------------------fork() --------------------------exit() ----------------------------wait()
多线程------------------------ pthread_create ---------pthread_exit() ----------------------pthread_join()

//1、线程的创建

#include <pthread.h>
int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
void pthread_exit(void *retval);
通常的形式为:
pthread_t pthid;
pthread_create(&pthid,NULL,pthfunc,NULL);pthread_create(&pthid,NULL,pthfunc,(void*)3);
pthread_exit(NULL);pthread_exit((void*)3);//3 作为返回值被后面的 pthread_join 函数捕获。
函数 pthread_create 用来创建线程。返回值:成功,则返回 0;失败,则返回-1。各参数描述如下:
 参数 thread 是传出参数,保存新线程的标识;
 参数 attr 是一个结构体指针,结构中的元素分别指定新线程的运行属性,attr 可以用 pthread_attr_init 等函数设置各成员的值,但通常传入为 NULL 即可;
 参数 start_routine 是一个函数指针,指向新线程的入口点函数,线程入口点函数带有一个 void *的参数由
pthread_create 的第 4 个参数传入;
 参数 arg 用于传递给第 3 个参数指向的入口点函数的参数,可以为 NULL,表示不传递。
函数 pthread_exit 表示线程的退出。其参数可以被其它线程用 pthread_join 函数捕获。

//2、线程的等待退出
线程本身的资源必须通过其它线程调用 pthread_join 来清除,这相当于多进程程序中的 waitpid。

线程从入口点函数自然返回,或者主动调用 pthread_exit()函数,都可以让线程正常终止
线程从入口点函数自然返回时,函数返回值可以被其它线程用 pthread_join 函数获取
pthread_join 原型为:
#include <pthread.h>
int pthread_join(pthread_t pthid, void **thread_return);

1. 该函数是一个阻塞函数,一直等到参数 pthid 指定的线程返回;与多进程中的 wait 或 waitpid 类似。
thread_return 是一个传出参数,接收线程函数的返回值。如果线程通过调用 pthread_exit()终止,则 pthread_exit()中的参数相当于自然返回值,照样可以被其它线程用 pthread_join 获取到。

2)、该函数还有一个非常重要的作用,由于一个进程中的多个线程共享数据段,因此通常在一个线程退出后,退
出线程所占用的资源并不会随线程结束而释放。如果 th 线程类型并不是自动清理资源类型的,则 th 线程退出后,

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/stat.h>
void *fun(void *p) //参数的值为 123
{
	int i = 0;
	for(; i<10; i++){
		printf("world,p = %d \n", (int)p);
		sleep(1);
	}
	pthread_exit(200);//线程结束,返回一个200的值
}
int main()
{
	pthread_t id, id2;
	int a=0,i=0;
	pthread_create(&id, NULL, fun, (void *)123 );
	for(; i<10; i++){
		printf("hello \n");
		sleep(1);
	}
	pthread_join(id, &a);//接收来自线程ID为id的返回的值
	printf("a=%d \n",a);
	return 0;
}

gcc -o a a.c -lpthread
在这里插入图片描述

//3、程序的取消
int pthread_cancel(pthread_t thread);
参数:thread线程的ID

线程也可以被其它线程杀掉,在 Linux 中的说法是一个线程被另一个线程取消(cancel)。
线程取消的方法是一个线程向目标线程发 cancel 信号,但是如何处理 cancel 信号则由目标线程自己决定,目标线程或者忽略、或者立即终止、或者继续运行至 cancelation-point(取消点)后终止
取消点:
根据 POSIX 标准,sleep,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及 read()、write()等会都会引起取消点 Cancelation-point。只要出现取消点,线程才从哪个位置退出出来


#include <stdio.h>
#include <pthread.h>
#include <malloc.h>
void* threadfunc(void *args)
{
	char *p = (char*)malloc(10); //自己分配了内存
	int i = 0;
	for(; i < 10; i++){
		printf("hello,my name is lirongye!\n");
		sleep(1);
	}
	free(p); //如果父线程中没有调用 pthread_cancel,此处可以执行
	printf("p is freed\n");
	pthread_exit((void*)3);
}
int main()
{
	pthread_t pthid;
	pthread_create(&pthid, NULL, threadfunc, NULL);
	int i = 1;
	//父线程的运行次数比子线程的要少,当父线程结束的时候,如果没有 pthread_join 函数
	//等待子线程执行的话,子线程也会退出。
	for(; i < 5; i++) {
		printf("hello,nice to meet you!\n");
		sleep(1);
		if(i % 3 == 0)
		 pthread_cancel(pthid); //表示当 i%3==0 的时候就取消子线程,该函数将导致子线程直接退
		//出,不会执行上面紫色的 free 部分的代码,即释放空间失败。要想释放指针类型的变量 p,此时必须要用
		//pthread_cleanup_push 和 pthread_cleanup_pop 函数释放空间,见后面的例子
	}
	int retvalue = 0;
	pthread_join(pthid,(void**)&retvalue); //等待子线程释放空间,并获取子线程的返回值
	printf("return value is :%d\n",retvalue);
	return 0;
}

在这里插入图片描述

4、线程终止清理函数
不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。最经常出现的情形是资源独占锁的使用:线程为了访问临界共享资源而为其加上锁,但在访问过程中该线程被外界取消,或者发生了中断,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

在 POSIX 线程 API 中提供了一个 pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源–从pthread_cleanup_push() 的 调 用 点 到 pthread_cleanup_pop() 之 间 的 程 序 段 中 的 终 止 动 作 都 将 执 行pthread_cleanup_push()所指定的清理函数。API 定义如下:
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理
void routine(void *arg)函数在调用 pthread_cleanup_push()时压入清理函数栈,多次对 pthread_cleanup_push()
的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute 参数表示程序自然执行到 pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为 0 表示不执行,非 0 为执行;这个参数并不影响异常终止时清理函数的执行。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是 pthread.h 中的宏定义:

#define pthread_cleanup_push(routine,arg) \
{ struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); }

可见,pthread_cleanup_push()带有一个"{",而 pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现
且必须位于程序的同一级别的代码段中才能通过编译。
pthread_cleanup_pop 的参数 execute 如果为非 0 值,则按栈的顺序注销掉一个原来注册的清理函数的时候,会执行该函数;当 pthread_cleanup_pop()函数的参数为 0 时,仅仅在线程调用 pthread_exit 函数或者其它线程对本线程调用 pthread_cancel 函数时,才在弹出“清理函数”的同时执行该“清理函数”。

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

void freemem(void * args)
{
	free(args);
	printf("clean up the memory!\n");
}
void* threadfunc(void *args)
{
	char *p = (char*)malloc(10); //自己分配了内存
	pthread_cleanup_push(freemem,p); //就相当于注册一个函数,在被取消的时候能调用这个函数
	int i = 0;
	for(; i < 10; i++){
		printf("hello,my name is lirongye!\n");
		sleep(1);
	}
	free(p); //如果父线程中没有调用 pthread_cancel,此处可以执行
	printf("p is freed\n");
	pthread_exit((void*)3);
	pthread_cleanup_pop(0);
}
int main()
{
	pthread_t pthid;
	pthread_create(&pthid, NULL, threadfunc, NULL);
	int i = 1;

	for(; i < 5; i++) {
		printf("hello,nice to meet you!\n");
		sleep(1);
		if(i % 3 == 0)
		 pthread_cancel(pthid); 
	}
	int retvalue = 0;
	pthread_join(pthid,(void**)&retvalue); //等待子线程释放空间,并获取子线程的返回值
	printf("return value is :%d\n",retvalue);
	return 0;
}

在这里插入图片描述

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值