【并发程序设计】6.线程创建

6.线程创建

线程概念

线程和进程在使用资源、创建开销以及通信方式上存在显著差异

首先,进程是系统资源分配的独立单位,每个进程拥有自己的地址空间,而线程则共享所隶属进程的地址空间。这意味着不同进程之间的资源如内存堆、栈是不能直接共享的,而同一进程内的多个线程可以直接访问这些共享资源。

其次,进程相较于线程有更大的创建和管理开销。因为进程有独立的地址空间,所以操作系统在创建或销毁进程时需要较大的系统资源开销。而线程作为调度的基本单位,其创建和上下文切换的开销要小得多,这也是为什么线程被称为轻量级进程的原因。

最后,由于线程间共享内存空间,它们之间的通信和数据共享更为简单直接。进程间则通常需要借助于进程间通信(IPC)机制,如管道、消息队列、共享内存等来完成数据交换。

线程特点

  • 通常线程指的是共享相同地址空间的多个任务
  • 使用多线程的好处
    • 大大提高了任务切换的效率
    • 避免了额外的TLB & cache的刷新

线程共享资源

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

  1. 可执行的指令
  2. 静态数据
  3. 进程中打开的文件描述符
  4. 当前工作目录
  5. 用户ID
  6. 用户组ID

线程私有资源

  1. 线程ID (TID)
  2. PC(程序计数器)和相关寄存器
  3. 堆栈
  4. 错误号 (errno)
  5. 优先级
  6. 执行状态和属性

线程创建

pthread_create 函数

  1. 原型

    #include <pthread.h>
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    
  2. 功能:创建一个新的线程,并使其开始执行指定的函数。

  3. 参数

    • pthread_t *thread:指向一个pthread_t类型的指针,用于存储新创建线程的标识符。
    • const pthread_attr_t *attr:指向一个pthread_attr_t类型的指针,用于设置线程的属性。如果传入NULL,则使用默认属性。
    • void *(*start_routine) (void *):指向一个函数指针,该函数将在新线程中执行。函数的返回类型为void *,参数为void *
    • void *arg:传递给start_routine函数的参数。
  4. 返回值

    • 成功时返回0
    • 失败时返回错误码。
  5. 注意:创建线程后,若主进程运行结束,它创建的线程也会随之结束。

    所以,要在主进程预留一段时间等待线程结束,避免线程中断。

错误解决

使用pthread_create函数时出现的错误:

test_createP.c:(.text+0x4b):对‘pthread_create’未定义的引用
collect2: error: ld returned 1 exit status

这个为链接错误,因为pthread_create函数的库为动态链接库
解决:在编译时加上 -lpthread
例:$gcc test.c -lpthread

线程结束

pthread_exit 函数

  1. 原型

    #include <pthread.h>
    void pthread_exit(void *retval);
    
  2. 功能:结束当前线程,线程私有资源被释放,并将返回值传递给其他线程

  3. 参数void *retval:指向一个指针,用于存储线程的返回值。该值可以被其他线程通过pthread_join()函数获取。

查看线程

pthread_self 函数

  1. 原型

    #include <pthread.h>
    pthread_t pthread_self(void);
    
  2. 功能:获取调用线程(自身所处线程)的标识符。

  3. 返回值:返回调用线程的标识符,类型为pthread_t

线程传参

线程间传参有两种方式,值传递地址传递

示例:arg1为地址传递,arg2为值传递

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

int * test_pth1(void *arg)
{
	printf("p1 tid = %lu\n",pthread_self());//线程ID
	printf("p1 pid = %d\n",getpid());//进程ID
	printf("p1 arg = %d\n",*(int *)arg);//传的参数
	pthread_exit(NULL);//退出线程
}
int * test_pth2(void *arg)
{
	printf("p2 tid = %lu\n",pthread_self());//线程ID
	printf("p2 pid = %d\n",getpid());//进程ID
	printf("p2 arg = %d\n",(int)arg);//传的参数
	pthread_exit(NULL);//退出线程
}
int main(int argc, const char *argv[])
{
	pthread_t tid;//线程ID
	int ret;
	int arg1,arg2;
	/*创建线程*/

	for(arg1=0; arg1<4; arg1++)
	{//地址传递
		ret = pthread_create(&tid,NULL,(void *)test_pth1,(void *)&arg1);
	}
sleep(1);
	for(arg2=0; arg2<4; arg2++)
	{//值传递
		ret = pthread_create(&tid,NULL,(void *)test_pth2,(void *)arg2);
	}
	printf("main tid%lu\n",tid);
	sleep(1);//等待线程结束
	return 0;
}

运行结果:

在这里插入图片描述

地址传递arg的值都为4,而值传递的arg值为创建线程时传入的值

地址传递的值会随着地址指向的数改变而改变,而线程创建的速度要比线程运行的速度要快,四个线程创建完后arg 的值为4,然后线程才开始运行,所以都打印出arg=4。

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值