任何人都可以告诉我如何在 C 函数内的随机位置插入函数调用(例如Yield()
),以便每次运行代码时Yield()
从代码的不同部分调用?
我遇到了这样的要求,因为我在协作线程环境中使用了2个线程,除非正在运行的线程明确地产生处理器,否则其他(等待)线程无法开始运行。我不想将Yield()
调用放在单个点上,因为这会使线程序列具有确定性。如果不重新布线整个环境(从合作到先发制人),这是我能想到的唯一解决方案,Thread_1()在其中的随机位置调用Yield()
,允许Thread_2()接管。
对于实现相同目标的不同解决方案的任何见解也是受欢迎的!
答案 0 :(得分:3)
我认为你必须以明显的方式解决这个问题。您将需要为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()的包装函数。