Linux - 线程特性举例

本文探讨了线程在程序设计中的关键特性,包括线程间共享全局变量、堆和栈资源的方式,展示了线程退出如何影响进程状态,并通过实例演示了多线程如何提高多核CPU的利用率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里主要演示线程的以下几个特性
  • 线程共享同一地址空间(全局变量,堆,栈)
  • 线程退出引起进程退出
  • 多线程充分利用多核CPU资源

同组线程共用同一地址空间

  • 全局变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//1.共享全局区资源
int g_count = 0;

void* ThreadEntry1(void* arg)
{
    (void)arg;
    while(1)
    {
        ++g_count;
        printf("t1 : %d\n",g_count);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    (void)arg;
    while(1)
    {
        ++g_count;
        printf("t2 : %d\n",g_count);
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,NULL);
    pthread_create(&t2,NULL,ThreadEntry2,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

上面代码中,有一个全局变量 g_count,两个线程,同时尝试去对 全局变量 g_count 进行修改
结果演示:

根据运行结果,我们可以看到,同组线程对全局变量是共享的

  • 堆上的变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//2.共享堆上的资源
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int* p = (int*)malloc(sizeof(int));
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

上面代码中,在main函数中malloc出了一段空间,这段空间在堆上,线程1和2同时去修改这个堆上的变量。


  • 栈上的变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//3.共享线程栈
//  使用栈上变量比较危险,我们使用前必须保证变量存在
//  但是主线程比较安全,因为他存在于整个进程
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int a = 0;
    int* p = &a;
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

代码中将主线程栈上一个变量a传给两个线程的线程入口函数,线程1和2分别随主线程栈上的元素进行改变。
注意:
修改栈上的资源是比较危险的,因为我们说过,每个线程都有自己的一个栈,这个但是这个栈是可以被访问也可以被改变的,试想,如果线程1已经退出了,线程2尝试去修改线程1栈上的元素,这是就很可能造成访问越界!但是,访问主线程栈上的元素相对比较安全,因为主线程栈是大家所共享的。




线程退出引起进程退出

线程组中某一个线程访存异常,都会导致进程异常终止。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//解引用空指针
//会导致硬件设备MMU发现这是一个异常操作
//会给操作系统发送一个异常
//操作系统内核就会给进程发送11号信号,导致进程异常终止 - 段错误

//解引用空指针使线程异常终止,观察
//线程一旦异常终止,就会导致整个进程都结束掉
void* ThreadEntry1(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t1 : %d\n",*p);
        int *ptr = NULL;
        *ptr = 10;
        sleep(1);
    }
    return NULL;
}

void* ThreadEntry2(void* arg)
{
    int* p = (int*)arg;
    while(1)
    {
        ++(*p);
        printf("t2 : %d\n",*p);
        sleep(1);
    }
    return NULL;
}

int main()
{
    int a = 0;
    int* p = &a;
    pthread_t t1,t2;
    pthread_create(&t1,NULL,ThreadEntry1,p);
    pthread_create(&t2,NULL,ThreadEntry2,p);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

线程尝试对空指针解引用时,进程终止。


多线程充分利用多核CPU资源

  • 场景1,创建对个线程不停歇工作,使用top指令查看CPU利用率
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//利用top指令查看cpu占用率
void* ThreadEntry(void* arg)
{
    (void)arg;
    while(1)
    {}
    return NULL;
}

int main()
{
    pthread_t tid[32];
    int n = 4;
    int i = 0;
    for(; i < n; ++i)
    {
        pthread_create(&tid[i],NULL,ThreadEntry,NULL);
    }
    for(i = 0; i < n; ++i)
    {
        pthread_join(tid[i],NULL);
    }
    return 0;
}

创建两个线程,这两个线程一直在死循环(可换成具体场景)。

如上图观察,CPU占用率可高达400%(四核CPU),证明我们充分利用了CPU资源。

### Linux Pthreads 继承属性和行为 #### 线程继承的概念 在Linux环境中,线程可以通过特定的方式继承其创建者的某些属性。这种机制允许子线程获得与父线程相同的调度参数和其他特性,从而简化了多线程应用程序的设计[^2]。 #### 设置线程继承权的方法 为了控制新创建的线程是否应该继承这些属性,程序员可以利用`pthread_attr_setinheritsched()`函数来进行配置。此方法接受两个参数:一个是指向`pthread_attr_t`类型的指针,另一个是指定继承模式的整数常量。如果选择了继承,则新线程将会复制父线程的调度策略;反之则不会[^1]。 ```c #include <pthread.h> int main(){ pthread_attr_t attr; int inherit = PTHREAD_INHERIT_SCHED; // 或者使用PTHREAD_EXPLICIT_SCHED pthread_attr_init(&attr); pthread_attr_setinheritsched(&attr, inherit); /* 创建具有指定属性的新线程 */ } ``` 上述代码展示了如何初始化并设置线程属性对象以改变即将创建的线程对于调度特性的处理方式[^5]。 #### 调度继承的具体实现细节 当设置了继承选项之后,在实际创建线程的过程中,系统会依据该设定来决定新线程应采用何种调度算法及其对应的优先级等信息。这不仅影响到CPU资源分配效率,也可能间接作用于整个应用性能表现上。 #### 实际应用场景举例说明 假设在一个实时音频处理软件里,主线程负责接收来自外部设备的数据流,并将其分发给多个工作线程去执行具体的编解码任务。此时如果我们希望所有的工作线程都能享有较高的响应速度以便及时完成各自的任务而不至于造成音视频不同步现象的发生,那么就可以让它们都继承自拥有高优先级别的主线程的调度属性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值