如何在多线程上下文中初始化静态变量?

时间:2011-01-22 11:57:04

标签: c windows multithreading static-initialization

我认为在函数中使用static关键字是这样的:

void threadSafeWrite(int *array, int writeIndex, int writeData){
    static void *threadLock = Lock_create(); //in my code locks are void* to be cross-platform compatable
    Lock_aquire(threadLock);
    array[writeIndex] = writeData;
    Lock_release(threadLock);
}

简而言之,这似乎是制作关键部分的好方法。我的问题是,如何以线程安全的方式初始化threadLock?我担心的例子的问题是锁将被多次分配,每个线程将使用不同的锁。有想法该怎么解决这个吗?看起来像鸡和蛋的问题。我想要一个与pthreads和windows线程一起使用的解决方案(或解决方案)。

编辑:我想要这个功能的原因是因为它提供了一种非侵入性的方法来测试运行一段代码是单线程还是多线程(用于调试目的)时是否存在差异。

4 个答案:

答案 0 :(得分:3)

无效,因为static变量的初始值设定项必须是C中的常量。函数调用不是常量。这与C ++不同,您可以在输入static之前使main执行工作。例如,这不会编译:

int deepthought()
{
    return 42;
}

void ask()
{
    static int answer = deepthought();
}

最简单的选择是使您的锁全局并在进入MT模式之前初始化它们。更好的选择是将它们与他们保护的数据一起传递。

P.S。:我建议不要使用void *来获得不透明的指针。相反,为类型安全实现特定于平台的单元素struct Lock

答案 1 :(得分:3)

一种方法是使用全局锁来序列化初始化路径。但是,您需要在SMP内存屏障上使用便携式包装器;锁定所暗示的获取障碍足够,因为它原则上允许编译器和/或CPU在锁定获取之前缓存存储器读取的结果。这是一个例子:

Lock global_init_lock; // Should have low contention, as it's only used during startup

void somefunc() {
    static void *data;
    static long init_flag = 0;
    if (!init_flag) { // fast non-atomic compare for the fast path
        global_init_lock.Lock();
        read_memory_barrier(); // make sure we re-read init_flag
        if (!init_flag)
            data = init_data();
        write_memory_barrier(); // make sure data gets committed and is visible to other procs
        init_flag = 1;
        global_init_lock.Unlock();
    }
    read_memory_barrier(); // we've seen init_flag = 1, now make sure data is visible
    // ....
}

那就是说,我建议把锁与数据放在一起,而不是放在对数据起作用的函数上。毕竟,你打算如何同步这样的读者?如果您希望以后为单独的数组使用单独的锁,该怎么办?如果你想编写其他功能,以后再开始使用该锁,该怎么办?

答案 2 :(得分:2)

Lock_create功能的整个想法被打破了;创建它的行为需要同步,你不能保证,因为你还没有锁定!不要使用指针锁。而是创建一个结构并将该结构的地址传递给锁定和解锁函数。或者更好的是,使用操作系统提供的操作系统,因为没有办法在普通的C中实现锁定。

每个人都说你需要把锁定为“全局”是错误的。只要它具有静态存储持续时间,功能级别范围就可以了。您只需要消除分配函数Lock_create并使用适当的类型进行锁定。

答案 3 :(得分:1)

有多种选择:

  • 在安全时间手动调用初始化代码(例如,在将代码暴露给其他线程之前)。
  • 使用pthread_once()或等效项,这将确保调用初始化代码一次,所有后续调用者都会看到它的效果。
  • 使用不涉及调用任何函数的锁的静态初始化。例如,使用pthreads

    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;