You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保多核心上两个线程的O1、O2操作精准并行执行?

核心间线程高精度同步执行问题

我编写了一个C程序,除主线程外创建了两个线程T1和T2。T1执行函数f1调用操作O1,T2执行函数f2调用操作O2,代码如下:

原始代码

线程函数f1

void* f1() {
    O1();
    var = 0;
}

线程函数f2

void* f2() {
    O2();
    var = 1;
}

主函数

int main(int argc, char **argv){
    pthread_t t1, t2;
    int var;

    pthread_create(&t1, NULL, &f1, NULL);
    pthread_create(&t2, NULL, &f2, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("var = %d\n", var);

    return 0;
}

程序目的是通过线程结束后var的值判断O1和O2哪个操作更快,这要求O1()和O2()在两个不同物理核心上几乎同时执行(仅允许几个周期的误差)。我该如何实现这一点?


编辑后的尝试(未成功)

根据建议,我修改了f1()和f2(),通过读取时间戳同步O1()和O2()的执行:

修改后的f1

void* f1() {
    t1 = rdtsc();
    while(t1 != 0){
        t1 = rdtsc();
    }   
    printf("t1 = %d\n", t1);
    O1();
    var = 0;
}

修改后的f2

void* f2() {
    t2 = rdtsc();
    while(t2 != 0){
        t2 = rdtsc();
    }   
    printf("t2 = %d\n", t2);
    O2();
    var = 1;
}

但控制台中t2的输出远晚于t1,这说明通过rdtsc循环到0的方式并未实现O1()和O2()的同步执行,且线程屏障无法满足我所需的同步粒度。


解决方案

要实现周期级的核心间线程同步,需要结合CPU同步指令、线程绑定和精确时间戳控制,避免调度延迟和指令重排的干扰。以下是具体实现方案:

1. 先绑定线程到指定物理核心

必须将T1和T2分别绑定到不同物理核心,防止调度器迁移线程导致的延迟。用sched_setaffinity实现:

#include <sched.h>

// 绑定线程到指定核心
void bind_to_core(pthread_t thread, int core_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
}

// 在main中创建线程后调用
bind_to_core(t1, 0); // 绑定到核心0
bind_to_core(t2, 1); // 绑定到核心1

2. 基于全局时间戳阈值的同步方案

设置全局目标时间戳,让两个线程先进入就绪状态,再同时等待时间戳达到阈值后执行操作,确保启动时机一致。

完整可运行代码

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

// 全局同步变量,volatile防止编译器优化
volatile uint64_t target_ts = 0;
volatile int ready_count = 0;
volatile int var;

// 测试操作O1、O2,替换为你的实际逻辑
void O1() { /* 你的操作 */ }
void O2() { /* 你的操作 */ }

// 读取序列化时间戳,保证指令顺序
static inline uint64_t rdtscp(void) {
    uint32_t lo, hi;
    __asm__ __volatile__ ("rdtscp" : "=a"(lo), "=d"(hi) : : "%rcx");
    return ((uint64_t)hi << 32) | lo;
}

void bind_to_core(pthread_t thread, int core_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
}

void* f1(void* arg) {
    // 原子递增就绪计数,确保线程安全
    __sync_fetch_and_add(&ready_count, 1);
    // 等待主线程设置目标时间戳
    while (target_ts == 0);
    // 循环等待直到时间戳达标
    while (rdtscp() < target_ts);
    // 执行操作
    O1();
    var = 0;
    return NULL;
}

void* f2(void* arg) {
    __sync_fetch_and_add(&ready_count, 1);
    while (target_ts == 0);
    while (rdtscp() < target_ts);
    O2();
    var = 1;
    return NULL;
}

int main(int argc, char **argv){
    pthread_t t1, t2;

    pthread_create(&t1, NULL, f1, NULL);
    pthread_create(&t2, NULL, f2, NULL);

    bind_to_core(t1, 0);
    bind_to_core(t2, 1);

    // 等待两个线程都进入就绪状态
    while (ready_count < 2);

    // 设置目标时间戳:当前时间+100000周期,预留足够时间让线程进入等待循环
    target_ts = rdtscp() + 100000;

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("var = %d\n", var);

    return 0;
}

3. 关键细节说明

  • volatile关键字:确保全局变量在多线程间的可见性,避免编译器优化导致的读取旧值问题。
  • __sync_fetch_and_add原子操作:保证ready_count的递增线程安全,不会出现计数错误。
  • 足够的等待周期:设置target_ts时额外加100000周期,是为了让两个线程都能进入等待循环,避免主线程设置目标后某个线程还未就绪就直接执行操作。
  • 避免IO干扰:测试阶段不要在同步后的代码中加入printf等IO操作,这类操作延迟极大,会完全破坏测试精度。

4. 可选增强:内存屏障

如果需要更强的内存序保证,可在等待循环后加入mfence指令,确保后续指令不会被重排到等待循环之前:

while (rdtscp() < target_ts);
__asm__ __volatile__ ("mfence" ::: "memory");
O1();

内容的提问来源于stack exchange,提问作者Sathvik Swaminathan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 18:37:13