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

发布于 2024-10-13 15:35:48 字数 573 浏览 7 评论 0原文

我想到了在函数内使用 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 线程的解决方案(或多个解决方案)。

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

I thought up a good use of the static keyword inside a function to be something like this:

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);
}

In short it seems like a good way to make a critical section. My question is though, how do I initialize the threadLock in a threadsafe manner? The problem with the example I fear is that the lock will get allocated multiple times and each thread will use a different lock. Any ideas on how to fix this? Seems like a chicken and egg problem. I want a solution (or solutions) which work with both pthreads and windows threads.

EDIT: the reason I want this functionality is because it provides a non-intrusive way to test whether there is a difference when running a piece of code single-threaded or multithreaded (meant for debug purposes).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

穿透光 2024-10-20 15:35:48

一种方法是使用全局锁来序列化初始化路径。然而,您需要一个 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
    // ....
}

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

One approach is to use a global lock to serialize the initialization paths. You'll need a portable wrapper over a SMP memory barrier however; the acquire barrier implied by the lock is not sufficient, as it, in principle, allows the compiler and/or CPU to cache the results of memory reads from before the lock acquisition. Here's an example:

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
    // ....
}

That said, I would recommend putting the locks with the data, not with the functions that act on the data. After all, how do you intend to synchronize readers like this? What if you want to use separate locks for separate arrays later? And what if you want to write other functions that take that lock later down the road?

给妤﹃绝世温柔 2024-10-20 15:35:48

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

int deepthought()
{
    return 42;
}

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

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

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

Won't work because the initializer of a static variable must be a constant in C. A function call is not a constant. This is different from C++, where you can make a static perform work before main is entered. For example, this won't compile:

int deepthought()
{
    return 42;
}

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

The simplest option is to make your locks global and initialize them before going into MT mode. A better option is to pass them along with the data they guard.

P.S.: I advise against using a void * to get an opaque pointer. Instead, implement a platform-specific, one-element struct Lock for type-safety.

苍风燃霜 2024-10-20 15:35:48

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

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

The whole idea of a Lock_create function is broken; the act of creating it would require synchronization, which you can't guarantee because you don't have a lock yet! Don't use pointers for locks. Instead create a struct and pass the address of that struct to your locking and unlocking functions. Or better yet, use the ones provided by your OS, since there's no way to implement locking yourself in plain C.

Also everyone who's said you need to make the lock "global" is wrong. Function level scope is fine as long as it's static storage duration, which you have. You just need to eliminate the allocation function Lock_create and use an appropriate type for the lock.

吃不饱 2024-10-20 15:35:48

有多种选择:

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

    静态 pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
    

There are various options:

  • Manually call the initialization code at a safe time (e.g. before exposing the code to additional threads).
  • Use pthread_once() or equivalent, which will ensure the initialization code is called once and all subsequent callers see its effects.
  • Use a static initialization of the lock that does not involve calling any function. For example, using pthreads

    static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文