我在多线程程序中使用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
不将生成器作为参数。有没有一种在多线程程序中使用这些非确定性函数的正确方法?
答案 0 :(得分:0)
这还不是一个真正的答案,但是评论太长了所以我将其作为答案发布。
igraph仍然无法保证线程安全性,尽管有些努力试图用线程本地替换全局变量。 (这就是IGRAPH_THREAD_SAFE
宏的作用)。
在内部,非确定性igraph函数需要RNG调用RNG_BEGIN()
宏来表示他们使用RNG的意图(然后调用RNG_END()
来表示他们不需要任何RNG更多)。 RNG_BEGIN()
调用igraph_rng_default()
来获取“默认”随机数生成器(如果这是第一次使用RNG,也会播种它)。 helgrind
确定的竞争条件可能表示同时由两个线程播种RNG。有趣的是,igraph_rng_default()
返回线程局部结构的地址(igraph_i_rng_default
,请参见源代码中的src/random.c
),此线程局部结构包含一个存储RNG是否存在的标志已经播种或播种。另一方面,RNG的状态似乎在线程之间共享,所以可能发生的是两个或更多线程试图弄乱相同的状态向量 RNG,因为所有线程都有自己的线程局部标志,表示RNG是否被播种。
如果我的预感是正确的,那么RNG的问题可以通过修补src/random.c
并通过在其声明中添加igraph_i_rng_default_state
使IGRAPH_THREAD_LOCAL
线程本地来解决。无论哪种方式,这绝对是当前版本的igraph中的一个错误,所以你应该在Github上file an issue。