Linux之线程的创建方式

下面详细讲解Linux线程的创建方式。

创建线程的方式

在Linux中,我们可以通过pthread库来创建线程,其中比较常用的三种方式分别是:

  1. 使用pthread_create函数来创建线程。
  2. 使用fork函数创建进程,然后使用pthread_create函数在新进程中创建线程。
  3. 使用clone系统调用来创建线程。

下面分别对这三种方式进行详细说明。

使用pthread_create函数创建线程

pthread_create函数的声明如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

其中,thread参数是一个指向pthread_t类型变量的指针,用于存储新创建线程的ID。attr参数用于指定线程的属性,一般使用默认属性即可,传入NULL即可。start_routine参数是线程的入口函数,arg参数是传给线程函数的参数,如果不需要传递参数,也可以传入NULL

下面是一个简单的例子,创建一个新线程来输出一个字符串:

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

void *my_thread_func(void *arg)
{
    char *str = (char *)arg;  // 将参数转换为字符串指针类型
    printf("%s\n", str);
    return NULL;
}

int main()
{
    pthread_t tid;
    char *str = "Hello, world!";

    // 创建一个新线程,执行my_thread_func函数,传入参数str
    pthread_create(&tid, NULL, my_thread_func, (void *)str);
    // 等待新线程结束
    pthread_join(tid, NULL);

    return 0;
}

在上面的例子中,我们使用pthread_create函数创建了一个新线程,并将其ID存储在了tid变量中。新线程执行的函数是my_thread_func,它的参数是一个字符串指针,指向字符串"Hello, world!"。在my_thread_func函数中,我们将传入的字符串打印出来。最后,使用pthread_join函数等待新线程结束。

使用fork函数和pthread_create函数创建线程

我们也可以使用fork函数创建子进程,然后在子进程中使用pthread_create函数创建线程。这种方式的优点是,可以避免线程之间的竞争问题。

下面是一个简单的例子,创建一个子进程,在子进程中创建一个新线程:

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

void *my_thread_func(void *arg)
{
    char *str = (char *)arg;  // 将参数转换为字符串指针类型
    printf("%s\n", str);
    return NULL;
}

int main()
{
    pid_t pid;
    pthread_t tid;
    char *str = "Hello, world!";

    // 创建子进程
    if ((pid = fork()) < 0) {
        perror("fork error");
        exit(1);
    }
    // 在子进程中创建新线程
    else if (pid == 0) {
        pthread_create(&tid, NULL, my_thread_func, (void *)str);
        pthread_join(tid, NULL);
    }

    return 0;
}

在上面的例子中,我们首先使用fork函数创建了一个子进程。在子进程中,我们再使用pthread_create函数创建了一个新线程,并将其ID存储在了tid变量中。然后,等待新线程结束。需要注意的是,这种方式创建的线程与主线程并不共享相同的地址空间,因此需要使用fork函数创建子进程。

使用clone系统调用创建线程

clone系统调用是Linux中创建线程的底层方法。下面是clone函数的定义:

#include <sched.h>

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...
           /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

其中,fn参数是线程的入口函数。child_stack参数指定新线程的栈空间,可以传入NULL,这样系统会自动为新线程分配栈空间。flags参数是标志位,用于指定新线程与父进程之间共享的资源,比如文件描述符、信号处理器等等。如果不需要共享资源,可以传入CLONE_VM标志位。arg参数是传给线程函数的参数,如果不需要传递参数,也可以传入NULL

下面是一个简单的例子,使用clone函数创建一个新线程:

#define _GNU_SOURCE  // 在使用clone前宏定义该宏
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define STACK_SIZE (1024 * 1024)

static char child_stack[STACK_SIZE];

void *my_thread_func(void *arg)
{
    char *str = (char *)arg;  // 将参数转换为字符串指针类型
    printf("%s\n", str);
    return NULL;
}

int main()
{
    pid_t pid;
    char *str = "Hello, world!";

    // 创建一个新线程
    pid = clone(my_thread_func, child_stack + STACK_SIZE, CLONE_VM | SIGCHLD, (void *)str);
    if (pid < 0) {
        perror("clone error");
        exit(1);
    }
    // 等待新线程结束
    waitpid(pid, NULL, 0);

    return 0;
}

在上面的例子中,我们首先宏定义了_GNU_SOURCE,这个宏是为了指定使用GNU的一些扩展函数,其中包括clone函数。然后,在main函数中,我们使用clone函数创建一个新线程,并将线程函数的地址作为第一个参数传入。新线程共享父进程的地址空间,因此传入CLONE_VM标志位。新线程的栈空间是child_stack数组的末尾,参数flags指定了在新线程创建后向父进程发送信号SIGCHLD。最后,使用waitpid函数等待新线程结束。

结束语

以上就是在Linux中创建线程的三种方式。其中,使用pthread_create函数是最常用的方法,也是最方便的方法。使用fork函数和pthread_create函数创建线程可以有效避免线程之间的竞争问题,但会导致代码复杂度提高。使用clone函数则是底层的创建线程方法,可以更加细粒度地控制新线程的行为。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Linux之线程的创建方式 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • 基于多线程中join()的用法实例讲解

    基于多线程中join()的用法实例讲解 1. 关于join()方法 在Python多线程编程中,join()方法是常用的多线程同步方法之一。该方法的作用是等待子线程结束后,再继续执行主线程。 2. join()方法的用法示例 示例1:基本用法 import threading def task(): print("Child thread star…

    多线程 2023年5月17日
    00
  • 聊聊java多线程创建方式及线程安全问题

    那么让我们来聊聊Java多线程创建方式及线程安全问题的完整攻略。 1. Java多线程的创建方式 Java中创建多线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。 1.1 继承Thread类 示例代码如下: class MyThread extends Thread { public void run() { System.out…

    多线程 2023年5月16日
    00
  • java实用型-高并发下RestTemplate的正确使用说明

    Java实用型 – 高并发下RestTemplate的正确使用说明 背景 RestTemplate 是 Spring 框架中非常常用的 HTTP 客户端,它可以轻松地进行 HTTP 请求和响应的处理。然而,当在高并发场景下使用 RestTemplate 时,容易导致线程阻塞、请求超时等问题。因此,本文将介绍如何在高并发场景下正确使用 RestTemplate…

    多线程 2023年5月17日
    00
  • Golang并发编程之Channel详解

    Golang并发编程之Channel详解 什么是Channel? 在Golang中,Channel是一种用于在不同的Goroutine之间进行通信和同步的机制。可以将其类比为管道。 在Golang中,一个Channel是一个类型为chan的引用类型。它是通过使用make函数创建的。 ch := make(chan int) // 创建一个类型为int的Cha…

    多线程 2023年5月17日
    00
  • C++中std::thread线程用法

    下面是详细讲解C++中std::thread线程用法的攻略。 C++中std::thread线程用法攻略 简述 C++11引入了std::thread库,使得多线程编程变得更加容易和便捷。 std::thread库提供了一种方便的方式来创建和控制线程,支持并发执行多个任务,在多核处理器上能够发挥出更好的性能。 在本攻略中,我们将详细讲解C++中std::th…

    多线程 2023年5月17日
    00
  • Java并发编程之阻塞队列(BlockingQueue)详解

    Java并发编程之阻塞队列(BlockingQueue)详解 什么是阻塞队列? 阻塞队列,顾名思义就是在队列的基础上加入了阻塞的特性。当队列满时,阻塞队列会自动阻塞写入线程,直到队列中有元素被移除,而当队列为空时,阻塞队列会自动阻塞读取线程,直到队列中有元素被添加。 Java中的阻塞队列是一个线程安全的队列,实现了如同锁的机制,可以保证多个线程同时访问是安全…

    多线程 2023年5月16日
    00
  • Python多线程与多进程相关知识总结

    Python多线程与多进程相关知识总结 多线程 多线程是指在同一进程中,多个线程并行执行不同的任务。Python提供了线程模块threading来处理多线程相关问题。线程模块允许开发商在单一进程内创建多个线程,从而最大限度地利用CPU资源。下面是一个简单的创建线程的示例代码: import threading def worker(num): "&…

    多线程 2023年5月17日
    00
  • Java多线程 线程状态原理详解

    Java多线程 线程状态原理详解 介绍 Java中的线程可以并行执行多个代码块,既可提高程序执行效率,又可防止程序因某些阻塞造成“卡死”。 线程状态就是指线程在代码执行期间所处的不同运行状态,进而影响着线程的执行顺序及资源分配。在Java中,线程状态主要由以下5种状态组成: 新建状态(New) 就绪状态(Runnable) 阻塞状态(Blocked) 等待状…

    多线程 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部