我的程序中创建了n
个线程。每个线程都从相同的“主线程函数”开始。代码会为该函数提供一个唯一的thread_index
,以便产生线程:
void worker_main_func(int thread_index);
vector<thread> spawn_workers(int n) {
vector<thread> workers;
workers.reserve(n);
for (int i = 0; i < n; ++i)
workers.emplace_back(worker_main_func, i);
}
return workers;
}
每个工作线程都需要访问一个专用队列。它们都是预先分配的。为了访问它的队列,一个线程需要知道它的 thread_index
:
static vector<my_queue_t> g_queues;
void do_some_work();
void worker_main_func(int thread_index) {
do_some_work();
}
void do_some_work {
// ...
g_queues[get_this_thread_index_somehow()].some_operation_on_queue();
// ...
}
我无法将thread_index
直接传递给do_some_work
,因为这需要几乎更改整个代码库。每个函数都需要带有一个额外的参数。对于当前通过寄存器传递其参数的函数,这可能会导致性能损失。使用新参数,它们可能需要在堆栈上传递其参数。
void do_some_work(int thread_index);
void calculate(int thread_index, /* params */);
void fetch_data(int thread_index, /* params */);
void implementation1(int thread_index, /* params */);
void blablabla(int thread_index, /* params */);
因此,我将
thread_index
存储在一个thread_local
变量中,并每次读取它:thread_local int g_thread_index;
void worker_main_func(int thread_index) {
g_thread_index = thread_index;
do_some_work();
}
void do_some_work {
// ...
g_queues[g_thread_index].some_operation_on_queue();
// ...
}
虽然这种方法可行,但并不是最优的。这是因为编译器生成的代码每次使用 g_thread_index
时都会从内存(或缓存)中读取它,并有时在其周围放置额外的初始化保护。同时,所有线程执行的工作都包含在 worker_main_func
中,这意味着 worker_main_func
及其参数始终可用 - 在堆栈底部:
--- inner_most_call ----
...
param2
param1
--- fetch_data ---------
param3
param2
param1
--- calculate1 ---------
--- do_some_work -------
thread_index
--- worker_main_func ---
因此,编译器可以直接从相对于当前线程堆栈的固定偏移量读取thread_index
,而不是从内存中读取。
我考虑使用std::this_thread::get_id()
代替我的thread_index
,但这会生成对pthread_self
的调用,并需要一些映射才能从不透明的thread::id
获取[0..n)
索引。