如何从另一个函数内的随机位置调用C函数?

时间:2009-12-11 05:10:20

标签: c multithreading random c-preprocessor

任何人都可以告诉我如何在 C 函数内的随机位置插入函数调用(例如Yield()),以便每次运行代码时Yield()从代码的不同部分调用?

我遇到了这样的要求,因为我在协作线程环境中使用了2个线程,除非正在运行的线程明确地产生处理器,否则其他(等待)线程无法开始运行。我不想将Yield()调用放在单个点上,因为这会使线程序列具有确定性。如果不重新布线整个环境(从合作到先发制人),这是我能想到的唯一解决方案,Thread_1()在其中的随机位置调用Yield(),允许Thread_2()接管。

对于实现相同目标的不同解决方案的任何见解也是受欢迎的!

5 个答案:

答案 0 :(得分:3)

需要一个BFI解决方案,我想

我认为你必须以明显的方式解决这个问题。您将需要为Yield()创建一个包装器,以便对是否调用真实内容做出“随机”决定。

如果你不关心执行速度那么我会把它变成一个真正的C函数,如果你是我可能会建议一个预处理器宏。

所以,比如:

#define Yield0() ((random() & 0xf) == 0 && Yield())

选择所需通话百分比的掩码。对于0xf,如果random()具有良好的低阶位随机性,那么您将在16次调用中看到1 Yield()。如果你可以使用MT或其他高质量的随机数发生器,低阶位将直接有用,否则你可能想要random() >> 3 & ...

你只需要在任何地方放置Yield0()调用。

答案 1 :(得分:2)

我定义了一个类似的函数:

void maybe_yield() { 
    if (rand() & 0x10)
        yield();
}

然后在整个代码中向maybe_yield()拨打电话。根据您希望调用yield的频率,您可以将0x10更改为常量,将更多位设置为更频繁地调用yield()。除此之外,请务必使用从一次运行更改为下一次运行的值来调用srand(),以便在不同的运行中获得不同的序列。

答案 2 :(得分:2)

实际上,当你在一个合作的线程环境中运行时,你真的想要决定论。

但是,如果你一心想做这件事,你只需要让它随机。

#include <stdlib.h>
// And make sure you seed the generator with srand() somewhere.
#define YIELD_CHANCE 15

#define yield Yield
#ifdef YIELD_CHANCE
    #if YIELD_CHANCE > 0
        #if YIELD_CHANCE <= 100
            #undef yield
            void yield(void) {
                if (rand() < (RAND_MAX / (100/YIELD_CHANCE)))
                    Yield();
                }
        #endif
    #endif
#endif

然后将Yield调用更改为yield,并且根据编译时设置的值YIELD_CHANCE,您将获得确定性或非确定性行为。

如果它不存在或超出1到100的范围,yield将一直产生。如果它在该范围内,那么它将根据您给出的概率随机调用Yield函数。

答案 3 :(得分:2)

选项A:当线程卡住时,为什么不调用yield()?更好的是,为什么不将其封装在可能会卡住的每个操作中:

int disk_read (...)
{
    begin_io ();
    while (!io_completed  &&  !timed_out())
         yield();
    if (timed_out())
        // etc.
     ...
}

选项B:通常 - 合作屈服 - 当另一个线程未准备好运行时,yield()是无操作。因此,把它放在任何地方:

void thread1 (...)
{
    yield();
    do_something_a();
    yield();
    do_something_b();
    yield();
    do_something_c();
    ...
}

选项C:相信处理器速度很快并且等待事情经常发生,以至于最小yields()工作正常:

void thread1 (...)
{
    init();
    while (...)
    {
        do_heavy_crunching();
        yield();
        do_something_else();
    }
}

在数百个真实应用程序中,Option C工作正常。决定论通常有帮助,而不是伤害。

答案 4 :(得分:1)

你说你不想要一个预处理器,但它让它变得如此简单。

   #!/usr/bin/perl
   chomp(my $n =<stdin>);
   open (my $f, '<', $n);
   while (my $l = <$f>) {
        print $l;
        if ($l =~ /^[\s][^\.]/) {
            $r=rand();
           if ( int($r*5) == 1 ) {
                print "\tcall Yield\n";
            }
        }
    }

这个perl脚本(我的第一个)将从stdin中读取文件名并随机插入一个调用gcc -S生成的程序集,然后可以轻松编译。它可能不适用于您的编译器/ arch,但正则表达式几乎可以做任何事情。

一个很好的补充是在处理器的跳转指令之前总是添加一个良率。这可以节省您的喷水量。最后在跳转之前,您可以使用调用random()的包装函数。