线程和进程

线程

为何要引入线程

多进程通讯的缺点有

  • 创建进程的过程会带来一定的开销
  • 为了完成进程间的数据交换,需要特殊的IPC技术
  • 每秒少则数十次,多则数千次的 上下文切换 是创建进程时最大的开销。

上下文切换:运行程序前需要将相应的进程信息读入内存,如果运行进程A后需要紧接着运行进程B,就应该将进程A相关信息移出内存,并读入进程B相关信息。

而多线程是一种轻量级进程,它对比进程有如下的优点。

  • 线程的创建和上下文切换比进程的创建和上下文切换更快。
  • 线程间交换数据时无需特殊技术。

当然多线程肯定也是有缺点的:

  • 每个线程与主程序共用地址空间,受限于2GB地址空间;
  • 线程之间的同步和加锁控制比较麻烦;
  • 一个线程的崩溃可能影响到整个程序的稳定性;
  • 到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;
  • 线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU

进程和线程的关系

每个进程都有 保存全局变量的数据区,向malloc等函数的动态分配提供空间的,函数运行时使用的构成。

进程的内存结构
可如果只需获得多个代码执行流为主要目的,这不应该创建如此多的进程结构,因为只需要分离站区域,因此在这里使用线程,结构如下:
线程的内存结果
采用线程会具有以下优势

  • 上下文切换时不需要切换数据区和堆
  • 可以利用数据区和堆交换数据

综上所述,可以总结进程和线程的联系。

  • 进程:在操作系统构成单独执行流的单位。
  • 线程:在进程构成单独执行流的单位。
    下面给出操作系统,进程,线程的关系图解:
    操作系统,进程,线程之间的关系

线程的资源共享

同一进程的线程以下资源共享

  • 进程代码段
  • 进程的公有数据(全局变量、静态变量…)
  • 进程打开的文件描述符
  • 进程的当前目录
  • 信号处理器/信号处理函数:对收到的信号的处理方式
  • 进程ID与进程组ID

线程独占哪些资源

  • 线程ID
  • 一组寄存器的值
  • 线程自身的栈(堆是共享的)
  • 错误返回码:线程可能会产生不同的错误返回码,一个线程的错误返回码不应该被其它线程修改;
  • 信号掩码/信号屏蔽字(Signal mask):表示是否屏蔽/阻塞相应的信号(SIGKILL,SIGSTOP除外)

线程的创建及运行

线程的创建

线程具有单独的执行流,因此需要单独定义线程的main函数,还需要请求操作系统在单独的执行流中执行该函数。

#include <pthread.h>

int pthread_create(pthread_t * restrict thread, const pthread_attr_t * restrict attr, void * (* start_routine)(void *), void * restrict arg);
//成功时返回0,失败时返回其他值。
  • thread 保存新创建的线程ID的变量地址值。
  • attr 用于传递线程属性的参数,传递NULL是,创建默认属性的线程。
  • start_routine 相当于线程main函数的,在单独执行流中执行的函数地址值(函数指针)。
  • arg 通过第三个参数传递调用函数是包含传递参数信息的变量地址值。

要理解好以上参数,则必须运用实例,但是我们知道,线程会伴随着进程的终结而终结,所以说,假如说我们线程还没执行完,而进程就结束了,我们就不能达到线程所执行的效果,所以说,我们要引用一个新的函数,调用该函数的进程(或线程)将进入等待状态。知道第一个参数为ID的线程终止为止。

pthread_join函数

#include <pthread.h>

int pthread_join(pthread_t thread, void ** status);
//成功时返回0,失败时返回其他值
  • thread 该参数值ID的线程终止后才会从该函数返回
  • status 保存线程的main函数返回值的指针变量地址值。
    此函数还能得到线程main函数的返回值到status,所以比较实用,下面给出应用实例。
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void* thread_main(void *arg);

int main(int argc, char *argv[])
{
	pthread_t t_id;
	int thread_param = 5;
	void * thr_ret;

	if(pthread_create(&t_id, NULL, thread_main, (void*)&thread_param)!=0)
	{
		puts("pthread_create() error");
		return -1;
	}

	if(pthread_join(t_id, &thr_ret)!=0)
	{
		puts("pthread_join() error");
		return -1;
	}

	printf("Thread return message: %s \n", (char*)thr_ret);
	free(thr_ret);
	return 0;
}

void* thread_main(void *arg)
{
	int i;
	int cnt = *((int*)arg);
	char * msg = (char*)malloc(sizeof(char)*50);
	strcpy(msg, "Hello, I'am thread~ \n");

	for(int i = 0;i < cnt;i++)
	{
		sleep(1);
		puts("running thread");
	}
	return (void*)msg;
}

运行结果如下图:
在这里插入图片描述
程序大概逻辑图如下:
在这里插入图片描述
线程的基础概念大致就是这样了,之后会更新线程同步有关知识,有问题欢迎在评论区提出哦!

Java中的线程进程是操作系统中两个基本的概念,它们在执行上下文中有着不同的含义: 1. **进程**(Process): 进程是操作系统分配资源的基本单位,每个进程都有自己的独立内存空间和系统资源。Java程序运行时会创建一个对应的JVM(Java Virtual Machine),JVM中可以有多个并发执行的线程进程的切换是由操作系统内核控制的,切换开销相对较高。 2. **线程**(Thread): 线程是在同一个进程中并行执行的轻量级实体,它们共享同一块内存空间,因此相比于进程线程之间的通信更快、切换更频繁。在Java中,每个Java应用至少有一个主线程,用户可以通过继承Thread类或实现Runnable接口来创建新的线程。 **主要区别**: - **资源占用**:进程拥有独立的内存空间,而线程共享进程的内存,所以线程更节省资源。 - **并发性**:同一进程内的线程可以直接相互访问数据,易于实现并发控制;而不同进程间的数据交换需要通过I/O操作。 - **调度粒度**:进程调度由操作系统进行,线程调度则由JVM或操作系统的用户级线程管理器完成,线程切换更快速。 - **同步与通信**:线程之间通常通过锁、条件变量等同步机制协作,而进程间的通信通常使用管道、消息队列等机制。 **相关问题**: 1. Java如何创建和管理线程? 2. 进程线程在性能优化上有什么考虑? 3. 在并发编程中,为什么要避免不必要的线程创建?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值