3

我想在函数中很好地使用 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?我担心这个例子的问题是锁会被多次分配,每个线程都会使用不同的锁。有想法该怎么解决这个吗?好像是先有鸡还是先有蛋的问题。我想要一个适用于 pthread 和 windows 线程的解决方案(或多个解决方案)。

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

4

4 回答 4

3

不起作用,因为static变量的初始化程序必须是 C 中的常量。函数调用不是常量。这与 C++ 不同,在 C++ 中您可以在输入static之前进行执行工作main。例如,这不会编译:

int deepthought()
{
    return 42;
}

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

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

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

于 2011-01-22T12:37:26.673 回答
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
    // ....
}

也就是说,我建议将锁与数据一起使用,而不是与作用于数据的函数一起使用。毕竟,你打算如何同步这样的读者?如果以后想对单独的数组使用单独的锁怎么办?如果您想编写其他函数在以后使用该锁怎么办?

于 2011-01-22T12:50:04.047 回答
2

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

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

于 2011-01-22T13:18:20.357 回答
1

有多种选择:

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

    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
    
于 2011-01-22T18:55:42.767 回答