我正在寻找有关重入的信息,然后我遇到了关于信号和线程的信息。这两者有什么区别?
请建议。
非常感谢。
答案 0 :(得分:5)
您正在比较苹果和橘子。 Signal Programming是事件驱动的编程,可用于影响线程。但是,信号编程范例可以在单线程应用程序中使用。
答案 1 :(得分:2)
要理解信号,最好先考虑一个单线程程序。该程序正在对其一个线程执行任何操作,然后将信号传递给它。如果程序已经为该信号注册了一个信号处理程序(一个要调用的函数),那么在调用信号处理函数时,该程序的正常执行将暂停一段时间(非常像硬件中断中断操作)系统运行中断服务程序)并运行程序已注册的功能来处理该信号。所以使用代码:
#include <stdio.h>
#include <signal.h>
#include <unistd.h> // for alarm
volatile int x = 0;
void foo(int sig_num) {
x = sig_num;
}
int main(void) {
unsigned long long count = 0;
signal(SIGALRM, foo);
alarm(1); // This is a posix function and may not be in all hosted
// C implementations.
// SIGALRM will be sent to this process in 1 second.
while (!x) {
printf("not x\n");
count++;
}
printf("x is %i and count = %llu\n", x, count);
}
程序将循环,直到某人向其发送信号(这种情况可能因平台而异)。如果发送信号SIGINT
,则foo
将设置x
,并且循环将退出。调用循环foo的确切位置尚不清楚。它可能发生在打印和递增计数之间,就在测试while条件之后,在打印期间......很多地方,真的。这就是信号可能造成并发或重入问题的原因 - 它们可以在没有其他代码知道它发生的情况下改变事物。
x
被声明为volatile的原因是没有那么多编译器可能会认为“嘿,主要没有人改变x而main没有调用任何其他函数,所以x永远不会改变”并优化出来循环测试。指定volatile
告诉C编译器可以通过看不见的力(例如信号处理程序,其他线程,有时甚至是内存映射设备控制寄存器中的硬件)来更改此变量。
很容易确保x
在信号处理程序和主执行代码之间正确查找,因为x
只是一个整数(它的加载和存储可能是单个在这种情况下,它只被一个 thing (信号处理程序,而不是主代码)改变,它只被用作一个简单的布尔值。如果x
是其他类型的字符串,那么由于信号可以随时打断你,因此当主代码处于读取字符串的中间时,信号处理程序可能会覆盖部分字符串。当你在刷牙的过程中,用眼镜蛇替换牙刷,然后解冻时间时,这可能会导致结果一样糟糕。
关于信号的更多信息 - 它们是C语言的一部分,但它们的大部分用途并未真正涵盖在C.许多与信号有关的Linux,Unix和POSIX函数都不是对于C语言,但很难得出合理的(和小的)信号使用示例,这些示例不依赖于C标准中没有的东西,这就是我使用alarm
函数的原因。 raise
函数是C的一部分,可以用来向自己发送信号,但是更难以为它做例子。
现在看起来像信号一样可怕,大多数系统都有更多功能,使它们更容易使用。
线程同时执行,而信号中断。虽然有一些线程库实际上以这种方式实现线程,但事实并非如此,最好以这种方式考虑线程。由于计算机程序实际上非常有限,因为它们能够看到正在发生的事情,所以线程可以以彼此的方式进入,就像信号处理程序可能妨碍主执行代码一样(尽管可能比信号处理程序更常见)。
想象一下,你将要再次刷牙,但这次你是盲目的。现在你的室友,也是def和盲人,用一些硅胶封口机来修理水槽。就像你伸手去拿牙膏一样,他将硅胶管放在牙膏管的顶部,然后抓住硅胶管而不是牙膏。请记住,因为你既是盲人又是def(并且不知何故不相互撞击)你们都认为没有其他人在使用水槽,所以你们从来没有意识到你只是把硅胶放在牙刷上,而你的室友也不会意识到他正试图用牙膏填补瓷砖和水槽背面之间的缝隙。
幸运的是,有些方法线程可以互相通信,目前正在使用某些东西,因此其他线程应该远离(比如在刷牙时锁上门)。