多线程与线程同步

多线程特点

线程是轻量级的进程,在Linux环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源。

可以理解为:进程是资源分配的最小单位,线程是操作系统调度的最小单位。

在概念上了解线程与进程的区别:

  • 进程有自己独立的地址空间,多个线程共用一个地址空间
    • 线程更加节省系统资源,效率不仅可以保持的,而且能够更高
    • 在一个地址空间中多个线程独享;每个线程都有属于自己的栈区、寄存器
    • 在一个地址空间中多个线程共享;代码段、堆区、全局变量区、文件描述符
  • 线程是程序最小的执行单位,进程是操作系统中最小的资源分配单位
    • 每个进程对应一个虚拟地址空间,一个进程只能抢一个CPU时间片
    • 一个地址空间中可以划分出多个线程,在有效资源基础上,能够抢更多的CPU时间片
  • 线程的CPU调度比进程快
    • 上下文切换:保存上一个任务的状态,下次切换回该任务时,加载这个状态继续运行,任务从保存到再次加载就是一次上下文切换
  • 线程更加廉价,启动速度更快,退出也快,对系统的冲击小

创建线程

线程函数

每个线程都有一个唯一的线程ID,ID类型为pthread_t(无符号长整型),使用pthread_self函数获取当前线程ID。

pthread_t pthread_self(void);  // 获取当前线程的线程ID

在一个进程中调用线程创建函数,就可以得到一个子线程,和进程不同,需要给每一个创建出来的线程指定一个处理函数,否则这个线程无法工作。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a
  • 参数
    • thread:传出参数,线程ID
    • attr:线程属性
    • start_routine:回调函数,函数指针
    • arg:传参
  • 返回值
    • 创建成功返回0

线程的退出

在编写多线程程序的时候,如果想要让线程退出,但是不能导致虚拟空间的释放,可说使用线程退出函数。只要调用该函数,线程马上退出,并且不会影响其他进程正常运行,不管是在子进程还是主进程都可以使用。

#include <pthread.h>
void pthread_exit(void *retval);
  • 参数:retval,线程退出时候要回传的参数

线程回收

线程函数

线程和进程是一样的,子进程退出的时候其内核资源主要有主线程回收,线程库中提供的线程回收函数叫做pthread_join,函数为阻塞函数,如果还有子线程运行,那么调用该函数会阻塞,子线程退出函数解除阻塞进行资源的回收,函数调用一次,只能回收一个子进程。

#include "pthread.h"
int pthread_join(pthread_t thread, void **retval);
  • 参数:
    • thread,回收的函数ID
    • arr,传出参数的地址,参数由pthread_exit传出

线程分离

在某种情况下,程序中的主线程有属于自己的业余处理流程,如果让主线程负责子进程的资源回收,调用pthread_t只要子进程不退出主进程就会一直被阻塞,主线程的任务就不能被执行了。

线程库提供了线程分离函数pthread_detach,调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源会被系统的其他进程接管并回收。使用线程分离之后,就无法在该进程释放子进程了。

#include "pthread.h"
int pthread_detach(pthread_t thread);
  • 参数:
    • read,分离的函数ID

线程取消

线程取消的意思是在特定情s况下在一个线程中杀死另一个线程,在线程A中调用线程取消函数,指定杀死线程B,此时线程B无法被杀死,在线程B中调用一次系统调用,从用户区切换到内核区,否则线程B可以一直运行。

#include <pthread.h>
int pthread_cancel(pthread_t thread);
  • 参数:thread:要杀死的线程ID

线程比较函数

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
  • 返回值:相同为非0,不相等为0

示例代码

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

/* 线程回调函数传参的结构体 */
typedef struct{
	char name[10];
	int num;
}thread_arg_t;

void *thread_callback(void *arg)
{
	thread_arg_t *demo = (thread_arg_t *)arg;

	// 输出自己的ID 
	printf("我是子线程,PID = %ld\n", pthread_self());
	for(int i = 0; i < 9; i++) {
		if(i == 5) {
			// 定义退出是的参数,这是使用static
			// 同一虚拟地址中的线程,不能共享栈区数据,但是可以共享全局数据区和堆区数据
			static thread_arg_t retval;
			strcpy(retval.name, demo->name);
			retval.num = demo->num;

			// 子线程退出
			pthread_exit(&retval);
		}
		printf("i = %d\n", i);
	}
	return NULL;
}

int main()
{
	pthread_t tid;
	thread_arg_t arg_t;
	strcpy(arg_t.name, "liustu");
	arg_t.num = 20;

	// 创建一个线程 参数 线程id,属性, 回调函数, 传参
	pthread_create(&tid, NULL, thread_callback, &arg_t);
	if(tid == 0) {
		printf("创建线程成功,PID:%ld\n", tid);
		printf("我是主线程,PID:%ld\n", pthread_self());
	}

	for(int i = 0; i < 3; i++) {
		printf("i = %d\n", i);
	}

	// 主线程等待,让自线程运行
	// sleep(2);
	
	// 主线程调用退出函数,地址空间不会被释放
	// pthread_exit(NULL);

	// 创建一个指针
	void *ptr = NULL;
	//  ptr 为传出参数,join函数内部让这个指针指向一个有效的内寸
	pthread_join(tid, &ptr);
	// 打印消息
	thread_arg_t *demo = (thread_arg_t *)ptr;
	printf("接收到子线程的数据:name %s num %d\n", demo->name, demo->num);


	return 0;
}
如果您觉得这篇文章不错,且手里较为宽裕,可以支持一下博主,一分也是缘分😊
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇