如何在多线程程序中使用非确定性 igraph 函数?

How can I use non-deterministic igraph functions in a multithreaded program?

我在多线程程序中使用 igraph C 库。每个线程使用 igraph_barabasi_game 创建一个随机图。尽管 IGRAPH_THREAD_SAFE 宏设置为 1,但我仍然遇到显然与线程相关的崩溃。

这是一个演示问题的最小程序。我从两个线程调用 do_work 函数,它只是创建一个随机图。

#include <pthread.h>                                                         
#include <igraph/igraph.h>                                                   

void *do_work(void *arg)                                                     
{                                                                            
    igraph_t g;                                                                 
    igraph_barabasi_game(&g, 100, 1, 4, NULL, 0, 1, 0, IGRAPH_BARABASI_PSUMTREE, NULL);
    igraph_destroy(&g);                                                      
}                                                                            

pthread_t threads[2];                                                        

int main (void)                                                              
{                                                                               
#if IGRAPH_THREAD_SAFE == 0                                                  
    fprintf(stderr, "igraph is not thread safe\n");                             
    return 1;                                                                   
#endif                                                                          

    pthread_create(&threads[0], NULL, do_work, NULL);                           
    pthread_create(&threads[1], NULL, do_work, NULL);                           
    pthread_join(threads[0], NULL);                                             
    pthread_join(threads[1], NULL);                                             

    return 0;                                                                   
}

这可以用gcc test.c -ligraph -lpthread编译。这个特定的程序没有崩溃,但是当我通过 helgrind 运行 它时,出现了与随机数生成器相关的错误。

==20144== Possible data race during write of size 8 at 0x547D038 by thread #3
==20144== Locks held: none                                                      
==20144==    at 0x4F172A5: igraph_rng_mt19937_get (random.c:354)                
    ==20144==    by 0x4F17358: igraph_rng_mt19937_get_real (random.c:381)       
    ==20144==    by 0x4F17951: igraph_rng_get_unif (random.c:805)               
    ==20144==    by 0x4ED3F68: igraph_i_barabasi_game_psumtree (games.c:381) 
    ==20144==    by 0x400828: do_work (in /home/rmcclosk/test/a.out)            
    ==20144==    by 0x4C30E26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==20144==    by 0x5488181: start_thread (pthread_create.c:312)              
    ==20144==    by 0x579847C: clone (clone.S:111)                              
    ==20144==                                                                   
    ==20144== This conflicts with a previous write of size 8 by thread #2       
    ==20144== Locks held: none                                                  
    ==20144==    at 0x4F17400: igraph_rng_mt19937_seed (string3.h:84)           
    ==20144==    by 0x4F177F1: igraph_rng_seed (random.c:684)                   
    ==20144==    by 0x4ED4354: igraph_i_barabasi_game_psumtree (games.c:340) 
    ==20144==    by 0x400828: do_work (in /home/rmcclosk/test/a.out)            
    ==20144==    by 0x4C30E26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==20144==    by 0x5488181: start_thread (pthread_create.c:312)              
    ==20144==    by 0x579847C: clone (clone.S:111)

我认为解决方案是将不同的随机数生成器对象传递给每个线程,但我在这里看不到这样做的方法,因为 igraph_barabasi_game 没有将生成器作为范围。在多线程程序中是否有正确的方法来使用这些非确定性函数?

更新:有关在多线程上下文中使用 igraph 的更新信息,请参阅文档的 Using igraph in multi-threaded programs 部分。


这还不是真正的答案,但对于评论来说太长了,所以我将其作为答案发布

尽管有人试图用线程局部变量替换全局变量,但 igraph 仍然不能保证线程安全。 (这就是 IGRAPH_THREAD_SAFE 宏的作用)。

在内部,需要 RNG 的非确定性 igraph 函数调用 RNG_BEGIN() 宏来表示他们打算使用 RNG(然后调用 RNG_END() 来表示他们不需要不再随机生成)。 RNG_BEGIN() 调用 igraph_rng_default() 来获取“默认”随机数生成器(如果这是第一次使用 RNG,也会为它播种)。 helgrind 识别的竞争条件可能代表两个线程同时对 RNG 进行播种。有趣的是,igraph_rng_default() returns 线程局部结构的地址(igraph_i_rng_default,请参阅源代码中的 src/random.c),并且这个线程局部结构包含一个标志存储 RNG 是否已经播种。另一方面,RNG 的 state 似乎在线程之间共享,因此可能发生的情况是两个或多个线程试图弄乱 [=34] 的状态向量=]相同 RNG,因为所有线程都有自己的线程局部标志,表示 RNG 是否已播种。

如果我的预感是正确的,那么 RNG 的问题可以通过修补 src/random.c 并通过在其声明中添加 IGRAPH_THREAD_LOCAL 使 igraph_i_rng_default_state 线程局部化来解决。无论哪种方式,这绝对是 igraph 当前版本中的一个错误,所以你应该 file an issue on Github.