如何在多线程程序中使用非确定性 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.
我在多线程程序中使用 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.