Linux中的线程(一)-- 线程的创建

线程是Linux中的轻量级进程,具有轻量级、调度、同步和线程安全等特点。与进程相比,线程共享地址空间,创建销毁成本低,上下文切换更快。通过pthread库可以创建和管理线程,如pthread_create和pthread_join。线程间的并发性高,但也存在资源竞争问题。
摘要由CSDN通过智能技术生成

什么是线程?

Linux 中的线程被称为“轻量级进程”(Lightweight Process,LWP),它是在进程内部运行的一种“子进程”。与传统的进程不同,线程共享相同的虚拟地址空间和其他资源,例如打开的文件、信号处理程序和用户 ID 等。

线程具有哪些特点

  1. 轻量级:
    因为线程与父进程共享资源,所以创建和销毁线程的开销要比创建和销毁进程的开销要小得多。
  2. 调度:
    Linux 线程是由内核进行调度的,线程的调度是基于调度策略和优先级来完成的。常见的调度策略包括 Round Robin、FIFO 和实时调度等。
  3. 同步:
    由于线程共享同一进程的地址空间,所以线程之间可以通过共享变量进行通信。Linux 提供了许多同步机制,例如信号量、互斥量和条件变量等,以确保线程之间的同步和互斥。
  4. 线程安全:
    Linux 系统提供了一些线程安全的库和函数,例如 pthread 库,这些库和函数可以在多线程程序中使用,避免竞态条件和其他线程安全问题。
  5. 调试:
    Linux 系统提供了一些工具来调试多线程程序,例如 gdb 调试器和 strace 工具。

线程与进程有什么区别

简单的说,进程是程序资源分配的最小单位,线程是程序执行的最小单位。

  1. 资源共享:
    进程是系统分配资源的基本单位,每个进程都有自己独立的地址空间、文件描述符、信号处理等资源,不同进程之间的资源不共享。而线程则共享同一个进程的地址空间和资源,它们之间可以相互访问共享变量,共享同一进程的文件和打开的网络连接等。
  2. 创建销毁开销:
    创建和销毁进程的开销比创建和销毁线程的开销要大。因为每个进程都有自己独立的地址空间和系统资源,创建一个新的进程需要操作系统分配新的内存空间、建立内核数据结构等,而线程则共享其所属进程的资源,因此创建和销毁线程的开销要比创建和销毁进程的开销要小得多。
  3. 上下文切换:
    线程之间的上下文切换比进程之间的上下文切换要快得多。因为线程共享相同的地址空间和资源,切换时只需要保存和恢复少量的寄存器状态即可,而进程之间的上下文切换需要保存和恢复更多的状态信息,比如虚拟内存空间等。
  4. 并发性和资源竞争
    线程之间的并发性比进程之间的并发性更高,因为它们共享同一进程的地址空间和资源,同时也会导致线程之间发生资源竞争的问题,例如竞争共享变量的访问权等。而进程之间的并发性相对较低,因为它们彼此独立。

如何创建一个线程

Talk is cheap,直接上代码!

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

using namespace std;


void *th_fun(void *arg)
{
    int *p = (int *)arg;
    printf("thread PID = %d\n",getpid());//获取本进程的ID
    printf("thread pthread ID = %d\n",pthread_self());//获取函数自己的ID
    printf("thread PARAMETER = %d\n",*p);//打印传入的参数
}

int main(void)
{
    pthread_t pid;//线程ID
    void *tret;
    int n=10;

    pthread_create(&pid,NULL,th_fun,(void*)&n);//pid 线程ID,传出参数
    pthread_join(pid, &tret);//主线程阻塞等待子线程的终止
    printf("MAIN pthread ID = %x\n",pthread_self());//主控线程的ID号
    printf("MAIN create pthread ID = %x\n",pid);//线程的ID
    printf("MAIN PID = %d\n",getpid());//进程ID

    return 0;
}

解读

主函数通过pthread_create函数创建一个线程,线程的函数名为th_fun,同时有一个pid参数代表线程的ID号,通过pthread_t类型定义。这里在pthread_create最后一个参数中,用了一个n变量负责传入一个数据给线程函数,然后在线程函数中打印出来。
pthread_join负责将线程设置为分离态,负责回收线程的资源。
主函数中分别打印了三个内容,主进程的线程的ID号,创建的新线程的ID号,主进程的ID号。
th_fun内容则简单的多,这个新创建的执行线程只负责打印三个内容:创建本线程的进程ID号,本线程自己的线程ID号,传入的arg参数。

输出

thread PID = 14420
thread pthread ID = 2
thread PARAMETER = 10
MAIN pthread ID = 1
MAIN create pthread ID = 2
MAIN PID = 14420

这里主进程的线程ID号为1,新创建的线程ID为2,该主函数执行时创建的进程ID为14420。
先打印出来thread线程的内容:因为线程是由main函数中创建的,所以进程ID同样为14420。新创建的线程ID和主函数中获取的线程ID一样为2,参数10是通过pthread_create的最后一个参数传进来的,这里要注意最后一个参数的类型为 void* 型,所以在赋值的时候也要将变量转换成 void* 型。

线程原语

实际上和线程有关的函数特别多,通过man命令可以查看到有哪些线程原语:

root@ubuntu:/home# man -k pthread
pthread_attr_destroy (3) - initialize and destroy thread attributes object
pthread_attr_getaffinity_np (3) - set/get CPU affinity attribute in thread attributes object
pthread_attr_getdetachstate (3) - set/get detach state attribute in thread attributes object
pthread_attr_getguardsize (3) - set/get guard size attribute in thread attributes object
pthread_attr_getinheritsched (3) - set/get inherit-scheduler attribute in thread attributes object
pthread_attr_getschedparam (3) - set/get scheduling parameter attributes in thread attributes object
pthread_attr_getschedpolicy (3) - set/get scheduling policy attribute in thread attributes object
pthread_attr_getscope (3) - set/get contention scope attribute in thread attributes object
pthread_attr_getstack (3) - set/get stack attributes in thread attributes object
pthread_attr_getstackaddr (3) - set/get stack address attribute in thread attributes object
pthread_attr_getstacksize (3) - set/get stack size attribute in thread attributes object
pthread_attr_init (3) - initialize and destroy thread attributes object
pthread_attr_setaffinity_np (3) - set/get CPU affinity attribute in thread attributes object
pthread_attr_setdetachstate (3) - set/get detach state attribute in thread attributes object
pthread_attr_setguardsize (3) - set/get guard size attribute in thread attributes object
pthread_attr_setinheritsched (3) - set/get inherit-scheduler attribute in thread attributes object
pthread_attr_setschedparam (3) - set/get scheduling parameter attributes in thread attributes object
pthread_attr_setschedpolicy (3) - set/get scheduling policy attribute in thread attributes object
pthread_attr_setscope (3) - set/get contention scope attribute in thread attributes object
pthread_attr_setstack (3) - set/get stack attributes in thread attributes object
pthread_attr_setstackaddr (3) - set/get stack address attribute in thread attributes object
pthread_attr_setstacksize (3) - set/get stack size attribute in thread attributes object
pthread_cancel (3)   - send a cancellation request to a thread
...

由于篇幅限制,后面的一部分就省略掉了,读者可以自行去查看。

那我们这里用到的几个和线程有关的线程原语有:

pthread_create

这个函数负责的是创建线程,通过man命令可以获取如何使用该函数。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

分别解释一下变量的含义:

  • pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)
  • const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL
  • void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块
  • void *arg:指定线程将要加载调用的那个函数的参数
  • 返回值:成功返回0,失败返回错误号。以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰
  • attr参数表示线程属性,本节不深入讨论线程属性,所有代码例子都传NULL给attr参数,表示线程属性取缺省值,感兴趣的读者可以参考[APUE2e]

pthread_join

调用该函数的线程将挂起等待,直到id为thread的线程终止。也就是在子线程调用了pthread_join()方法后面的代码,只有等到子线程结束了才能执行。

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
  • pthread_t thread:回收线程的tid
  • void **retval:接收退出线程传递出的返回值
  • 返回值:成功返回0,失败返回错误号

注意,这里其实还涉及到与pthread_detach()函数的区别对比。在后续的文章再加以对比说明。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值