12-7 Thread Local 线程变量(部分待定)

1. _Thread_local

        _Thread_local 可以将指定变量当做线程的内部变量,避免线程之间的变量共享。以 12-3 节的 count++ 为例,若 count 变量是每个线程内部的变量,则不会出现线程安全问题。故将代码修改如下:

#include <stdio.h>
#include <tinycthread.h>

// 创建线程生命周期的变量 main_count,线程结束时自动释放
_Thread_local int main_count = 0;

int Counter(int *count){

  for (int i = 0; i < 100000; ++i) {
    main_count += *count;
  }

  printf("main_count: %d\n", main_count);
  return 0;
}

void TestThread(){

  thrd_t t1;
  thrd_t t2;

  int count_t1 = 1;  // t1 线程加 10w 次的 1
  int count_t2 = 2;  // t2 线程加 10w 次的 2

  thrd_create(&t1, Counter, &count_t1);
  thrd_create(&t2, Counter, &count_t2);

  thrd_join(t1, NULL);
  thrd_join(t2, NULL);

  printf("main_count: %d\n", main_count);
}

        注意,每个线程都会有 main_count 变量,原因在于 _Thread_local 修饰的变量在每个线程创建之初就会被同时创建。其中,主线程的 main_count 变量没有做任何操作,子线程 t1 的 main_count 变量增加了 10w 次的 1,而子线程 t2 的 main_count 变量增加了 10w 次的 2。故程序执行结果为:

 2. tss_t

        使用 tss_t 及相关函数 tss_set(),tss_create(),tss_delete(),将指定内存绑定为线程内部内存,此时内存对应的变量即为线程内部变量。用此方法同样可以解决 12-3 小节的 count++ 问题。

        整体逻辑如下:利用 tss_t 定义一个 key,再使用 tss_create() 函数创建 key(类比于锁的定义和初始化)。而后开辟一块内存,利用 tss_set() 函数将 key 与该块内存绑定,此时该内存为线程内部内存。该内存对应的变量即为线程内部变量,可任意操作。待操作完成后,使用销毁函数将该内存进行销毁,而后使用 tss_delete() 函数将 key 销毁。

        使用 tss_t 后,每个线程都会有自己的 key ,内部内存和内部变量,不再使用共享资源。

#include <stdio.h>
#include <tinycthread.h>
#include <stdlib.h>

tss_t count_key;  // 定义线程内部的一个key

int Counter(int *count){

  int *main_count = malloc(sizeof(int));  // 开辟一块内存
  *main_count = 0;

  // 将内存与线程内部的 key 进行绑定,此时内存为线程内部的内存
  if(tss_set(count_key, main_count) == thrd_success){
    for (int i = 0; i < 100000; ++i) {
      *main_count += *count;  // 每个线程都有自己的 main_count 变量和 *main_count
    }
  }

  printf("*main_count: %d\n", *main_count);
  printf("*((int *)tss_get(count_key)): %d\n", *((int *)tss_get(count_key)));
}

// 释放 ptr 指针指向的内存
void MyFree(void *ptr){
  printf("free prt:%#x\n", ptr);
  free(ptr);
}

void TestTss(){

  // 创建一个 key, 并传入将 key 对应内存销毁的函数 MyFree
  if(tss_create(&count_key, MyFree) == thrd_success){

    thrd_t t1;
    thrd_t t2;

    int count_t1 = 1;
    int count_t2 = 2;

    thrd_create(&t1, Counter, &count_t1);
    thrd_create(&t2, Counter, &count_t2);

    thrd_join(t1, NULL);
    thrd_join(t2, NULL);

    puts("t1,t2 ended.");

    // 此时线程停止,key指向的内存被释放,下一步删除key
    tss_delete(count_key);

    // 主线程的 main_count
    printf("*((int *)tss_get(count_key)): %d\n", *((int *)tss_get(count_key)));

  }else{
    perror("result");
  }
}

        每个线程都会有自己的 main_count 指针以及对应的变量值,不再出现线程安全问题。

        遗憾的是,由于源码中 tss_create() 函数有一个逻辑目前无法解决,故最终程序执行结果有误。tss_create() 函数接收两个参数,第一个是 key 的地址,第二个是 key 对应内存的销毁函数,而源码中需要销毁函数为 NULL 时才能返回 thrd_success,不为 NULL 时返回 thrd_error。

        此时逻辑出现 bug。不传入销毁函数,dtor == NULL,可以成功创建 key,但无法销毁 key 对应内存;传入销毁函数,dtor != NULL,可以销毁内存,但无法成功创建 key。此问题目前待定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值