【Linux多线程】详解线程控制、线程分离

00

👸 理解线程

🤴pthead_t

pthread_t 是 POSIX 线程库中的数据类型,用于表示线程标识符。POSIX(Portable Operating System Interface for Unix)是一套标准,定义了在 UNIX 系统中的应用程序编程接口(API)规范,其中包含了线程操作的标准接口。

在多线程编程中,每个线程都有一个唯一的标识符,用于区分不同的线程。pthread_t 就是用来存储这个线程标识符的数据类型。它在 <pthread.h> 头文件中定义。

在使用 POSIX 线程库创建线程时,会得到一个 pthread_t 类型的变量,用于标识新创建的线程。您可以使用 pthread_t 变量来操作、控制或等待特定的线程。通常情况下,我们通过调用 pthread_create 函数来创建新线程,并将新线程的标识符保存在 pthread_t 变量中。

🥷关于线程

关于线程我们要知道以下概念

  1. 线程是一个独立的执行流
  2. 线程一定会在自己的执行过程中产生临时数据(调用函数,定义局部变量等等)
  3. 线程一定有自己的独立栈结构

🦸‍♀️线程控制

POSIX线程库

  1. 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  2. 要使用这些函数库,要通过引入头文<pthread.h>
  3. 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
  • 创建

功能:创建一个新的线程
原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(start_routine)(void), void *arg);
参数
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

  • 错误检查:

传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通
过返回值返回
pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,
建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小

线程ID及进程地址空间布局

pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要
一个数值来唯一表示该线程。
pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,
属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:
pthread_t pthread_self(void);

pthread_t 到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

🦸线程分离

  • 代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include<iostream>
using namespace std;
  int N=1000;
void* threadF1(void *argv)
{
    while (true)
    {
        cout<<"thread:"<<pthread_self()<<"   "<<"N的值:"<<N<<"   "<<"&N:"<<"   "<<&N<<"Inc:"<<N++<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t td1;
    pthread_t td2;
    pthread_t td3;
    pthread_create(&td1,nullptr,threadF1,(void*)"td1");
    pthread_create(&td2,nullptr,threadF1,(void*)"td2");
    pthread_create(&td3,nullptr,threadF1,(void*)"td3");

    pthread_join(td1,nullptr);
    pthread_join(td2,nullptr);
    pthread_join(td3,nullptr);




    return 0;
}
  • 结果
    00
    总结起来就是 共享的资源N 在进行++操作的时候 多个线程访问的是同一个N 那么怎么让线程分离呢?

__thread关键字

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include<iostream>
#include <sys/syscall.h>
using namespace std;
  __thread int global_value=1000;
void* threadF1(void *argv)
{
    while (true)
    {
       cout << "thread " << pthread_self() << " global_value: "
            << global_value << " &global_value: " << &global_value
              << " Inc: " << global_value++ << " lwp: " << ::syscall(SYS_gettid)<<endl;
         sleep(1);
      // mak   break;
    }
}
int main()
{
    pthread_t td1;
    pthread_t td2;
    pthread_t td3;
    pthread_create(&td1,nullptr,threadF1,(void*)"td1");
    pthread_create(&td2,nullptr,threadF1,(void*)"td2");
    pthread_create(&td3,nullptr,threadF1,(void*)"td3");

    pthread_join(td1,nullptr);
    pthread_join(td2,nullptr);
    pthread_join(td3,nullptr);




    return 0;
}

看把共享数据块global_value用 __thread修饰即可 !

  • 结果:
    00

🦸‍♂️pthread_detach函数

pthread_detach 函数用于将一个线程标记为“可分离”的状态。当一个线程被标记为“可分离”,则在该线程终止时,线程的资源会自动释放,而无需其他线程调用 pthread_join 来等待和回收该线程的资源。

具体来说,pthread_detach 函数用于向线程库指示,当目标线程(被调用 pthread_detach 的线程)终止时,其状态和资源可以被系统回收,而不需要其他线程调用 pthread_join 来等待其终止。

使用 pthread_detach 的好处是,它可以防止资源泄漏。如果不使用 pthread_detach,当一个线程终止时,它的资源将一直保留在系统中,直到其他线程调用 pthread_join 来回收它的资源。如果没有及时回收资源,可能会导致资源泄漏。

在实际应用中,通常将不需要回收线程资源的线程标记为“可分离”,而将需要回收资源的线程标记为“非分离”。标记线程为“非分离”后,其他线程可以使用 pthread_join 来等待它的终止,并回收其资源。

要使用 pthread_detach,只需在目标线程中调用它即可,例如

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

void* threadFunction(void* arg) {
    // Thread logic
    printf("Thread function executed.\n");
    return NULL;
}

int main() {
    pthread_t thread;
    if (pthread_create(&thread, NULL, threadFunction, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }

    // Mark the thread as detachable
    if (pthread_detach(thread) != 0) {
        perror("pthread_detach");
        return 1;
    }

    // Continue with other tasks, no need to call pthread_join.

    // Sleep to give the thread time to execute before the program exits
    sleep(1);
    return 0;
}

在上述示例中,pthread_detach 函数被调用,将线程 thread 标记为“可分离”的状态。因此,我们不需要在主线程中调用 pthread_join 来回收线程资源。在主线程中可以继续进行其他任务,线程 thread 的资源会在它终止时自动释放

🦹‍♀️pthread_exit函数

pthread_exit 函数用于在线程内部终止当前线程的执行,并返回一个指定的退出值。这个函数允许线程在任何地方终止,而不必等待线程的函数返回。

函数原型:

#include <pthread.h>

void pthread_exit(void *retval);

参数 retval 是一个指向任意类型的指针,它表示线程的退出值。这个值可以被其他线程通过 pthread_join 函数获取到。

当一个线程调用 pthread_exit 函数时,它会立即终止当前线程的执行,并将 retval 指向的值传递给等待它的线程。

pthread_exit 的使用场景包括:

在线程执行完任务后,通过 pthread_exit 终止线程,而不是返回到线程的创建点。
在线程内部发现错误或条件,需要立即终止线程的执行。
在线程执行过程中遇到某种情况需要立即退出,并向其他线程传递一些信息。
以下是一个简单的示例,演示了 pthread_exit 的使用:

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

void* threadFunction(void* arg) {
    // Thread logic
    printf("Thread function executed.\n");

    int exitValue = 42;
    pthread_exit((void*) &exitValue);
}

int main() {
    pthread_t thread;
    if (pthread_create(&thread, NULL, threadFunction, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }

    void* returnValue;
    if (pthread_join(thread, &returnValue) != 0) {
        perror("pthread_join");
        return 1;
    }

    // Cast the return value back to its original type
    int exitValue = *(int*) returnValue;
    printf("Thread exit value: %d\n", exitValue);

    return 0;
}

在上述示例中,线程函数 threadFunction 调用 pthread_exit 来终止线程的执行,并传递了一个整数值作为退出值。主线程通过 pthread_join 来等待线程的结束,并获取线程的退出值,然后打印出来

🦹 exit和pthread_exit

pthread_exit 和 exit 都可以用于终止程序的执行,但它们之间有几个关键的区别:

作用范围:

pthread_exit 仅用于终止调用它的线程的执行,不会终止整个进程。
exit 会立即终止整个进程的执行,包括所有线程。
参数传递:

pthread_exit 允许在线程内部传递一个指向任意类型的指针作为退出值,其他线程可以通过 pthread_join 来获取这个退出值。
exit 的退出值必须是整数类型,它会作为进程的退出状态传递给操作系统。
资源回收:

pthread_exit 不会自动释放线程占用的资源,因此需要在适当的地方手动释放资源。
exit 会自动释放整个进程占用的资源,包括打开的文件、动态分配的内存等。
使用场景:

pthread_exit 通常用于线程内部,在线程完成任务后主动退出,或者在线程内部遇到错误时终止线程的执行。
exit 通常用于整个程序,在程序完成主要任务后终止整个进程的执行,一般在 main 函数的末尾或者需要提前退出程序的地方使用。
注意:在多线程程序中,使用 exit 可能会导致一些问题,因为它会立即终止整个进程,可能会导致其他线程的资源无法正确释放,从而造成资源泄漏或未定义行为。在多线程程序中,推荐使用 pthread_exit 来终止线程的执行,以保证资源的正确释放。
00

🦹‍♂️ 🤶 🧑‍🎄 🎅 🧙‍♀️ 🧙 🧙‍♂️ 🧝‍♀️ 🧝 🧝‍♂️ 🧛‍♀️ 🧛 🧛‍♂️ 🧟‍♀️ 🧟 🧟‍♂️ 🧞‍♀️ 🧞 🧞‍♂️ 🧜‍♀️ 🧜 🧜‍♂️ 🧚‍♀️ 🧚 🧚‍♂️ 👼

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来详细解释一下多线程。 在一个程序中,通常会有多个任务需要同时执行,而且有些任务可能需要很长的时间才能完成。如果所有的任务都在一个线程中执行,就会出现程序“卡住”的情况,即程序处于一种等待状态,无法响应用户的操作,用户体验非常差。为了解决这个问题,就需要使用多线程技术。 多线程是指在一个程序中同时运行多个线程,每个线程都可以独立执行不同的任务。每个线程都有自己的堆栈、局部变量和程序计数器等线程私有的资源,但是它们共享进程的其他资源,比如全局变量和静态变量等。多线程可以让程序更加高效,能够同时处理多个任务,提高系统的响应速度和并发能力。 在 C# 中,可以使用 System.Threading 命名空间中的 Thread 类来创建和管理线程。下面是使用 Thread 类创建和启动一个线程的示例代码: ``` using System; using System.Threading; public class Program { public static void Main() { Thread t = new Thread(new ThreadStart(DoWork)); t.Start(); } public static void DoWork() { // 线程要执行的任务 } } ``` 上面的代码中,我们创建了一个名为 t 的线程,并将其启动。线程要执行的任务定义在 DoWork 方法中。 在多线程编程中,需要注意线程之间的同步问题,以避免出现数据竞争和死锁等问题。比如,多个线程可能同时访问同一个共享变量,如果不加以控制,就会出现数据竞争的问题。C# 中提供了多种线程同步机制,比如 lock、Monitor、Semaphore 等,可以用来保护共享资源,避免数据竞争问题的发生。 此外,多线程还有一些常见的问题,比如线程池的使用、线程的优先级、线程的异常处理等等。需要开发人员了解和掌握这些知识,才能写出可靠、高效的多线程程序。 总之,多线程是一种非常重要的编程技术,可以提高程序的效率和性能,但是也需要开发人员具备一定的编程经验和技能,才能正确地使用它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值