13

对所有人来说,这可能是一个非编程问题,我确实阅读了有关线程同步对象(例如事件)以及如何将其设置为信号或非信号状态的信息。但是我无法理解这些有信号和无信号的术语。每个术语 都以不同的方式表达,我有点困惑。

  1. 该链接指出

    信号状态表示资源可供进程或线程使用。无信号状态指示资源正在使用中。

  2. 我从一个大学网站获得了一份简报,其中指出

    处于信号状态的对象不会导致等待该对象的线程阻塞,而不处于信号状态的对象将导致等待该对象的任何线程阻塞,直到该对象再次变为信号状态。

  3. 这第三个链接说明了这一点

    一个事件处于有信号状态意味着它有能力释放等待这个事件发出信号的线程。一个事件处于非信号状态意味着它不会释放任何正在等待这个特定事件的线程。

用一个例子简单地解释这个概念会很有帮助。

4

4 回答 4

24

好的,您的 3 个报价并非不兼容。但是让我们来看看实现:

每个可等待对象都有一个附加的布尔值,称为信号状态,用于等待该对象;如果对象是有信号的,那么等待函数将不会等待它;如果对象是non-signaled,则等待函数等待它。

现在,这如何应用于特定类型的对象?这取决于对象的性质,特别是与等待它相关的语义。实际上,信号状态是根据等待条件定义的。例如(有关详细信息,请参阅文档):

  • 当互斥锁不被拥有时,它会发出信号。
  • 进程/线程完成时会发出信号。
  • 信号量在其计数大于 0 时发出信号。
  • 等待计时器到期时会发出信号。

如果互斥体在拥有时发出信号,您可能会更喜欢,但实际上它是在不拥有时发出的。这对于让等待函数做正确的事情是必要的。

那么事件呢?嗯,它们是一些简单的对象,你可以随意给它们发信号和去信号,所以信号状态没有额外的意义:

  • 发出信号:线程不会等待它。
  • 非信号:线程将等待它。

事件也有这个SignalPulseAutoReset一些有点奇怪的东西(而且 IME 实际上不可能正确使用)。

现在,让我们看看你的报价:

信号状态表示资源可供进程或线程使用。无信号状态指示资源正在使用中。

其实,这是一种解释。通常有一个资源是你试图仲裁的,并且通常你会等待当且仅当该资源正在使用中,所以它在使用资源和等待资源之间进行等价。但这不是技术要求,只是一个常见的用例。

处于信号状态的对象不会导致等待该对象的线程阻塞,而不处于信号状态的对象将导致等待该对象的任何线程阻塞,直到该对象再次变为信号状态。

正确且切中要害!

一个事件处于有信号状态意味着它有能力释放等待这个事件发出信号的线程。一个事件处于非信号状态意味着它不会释放任何正在等待这个特定事件的线程。

我觉得这个措辞有点令人困惑……但它与前一个相比并没有增加任何内容。

于 2013-07-12T11:19:25.587 回答
16

简单的想法:“信号”=“绿灯”

绿灯 Signaled:如果您正在开车并且看到绿灯,则不会停下来(这是查看事件的线程,发现它已发出信号并继续进行而不会阻塞)。

红灯 无信号:如果你看到红灯,你停下来等待它变成绿色然后继续(安全地知道其他线程现在都是无信号的,因此正在等待或将在它们的...红灯处等待!)

于 2013-07-12T13:24:29.230 回答
8

嗯,事实上所有这些解释都是一致的。

对事件的最简化(因此不是 100% 准确)的解释是将事件视为操作系统提供的一种标志服务。有信号的事件可以看作是一个设置标志,另一方面,一个未发出信号的事件可以看作是一个未设置的标志。

为了实现基于标志的生产者/消费者线程系统,您通常执行以下操作(注意为简单起见,我忽略了进一步的同步机制):

static volatile int  flag = 0;
static volatile char data = 'A';

// Some code to initialize the threads  

void producer()
{
    while (1)
    {
        Sleep(1000);
        data++;
        flag = 1;
    }
}

void consumer()
{
    while (1)
    {
        /* Busy wait for the occurence of more data */
        while (!flag)
        {
            // wait for next data
        }

        flag = 0;

        // process data
    }
}

不幸的是,这会导致在繁忙的等待循环中浪费处理器周期,或者由于Sleep引入了降低 CPU 消耗的调用而导致不必要的执行延迟。两者都是不受欢迎的。

为了避免任务同步的此类问题,操作系统提供了不同的类似标志的机制(例如 Windows 中的事件)。对于事件,设置和重置标志由操作系统调用SetEvent/完成ResetEvent。要检查标志,您可以使用WaitForSingleObject. 此调用有权使任务进入睡眠状态,直到发出事件信号,这在 CPU 消耗方面是最佳的。

这会将上面的示例变成如下所示:

static volatile char data = 'A';
static HANDLE newDataEvent = INVALID_HANDLE_VALUE;

// Some code to initialize the threads and the newDataEvent handle  

void producer()
{
    while (1)
    {
        Sleep(1000);
        data++;
        SetEvent(newDataEvent);
    }
}

void consumer()
{
    while (1)
    {
        if (WaitForSingleObject(newDataEvent, INFINITE) == WAIT_OBJECT_0)
        {
            ResetEvent(newDataEvent);
            // process data
        }
    }
}
于 2013-07-12T11:23:17.863 回答
0

我不太同意其他答案。他们没有抓住重点:

  • 如果信号属性为true => 事件发生在 now 之前

  • 如果信号属性为false => 事件直到现在发生。

其中“信号属性为”等于“非信号属性为”。

而且这三个定义都是指线程,但并不清楚,因为信号定义不是来自多线程,而是来自低级编程。

信号来自中断:
如果该信号变为高电平(=中断),则将执行指针移动到该函数”。
这就是信号的含义,它来自中断而不是线程。因此,未发出信号的意思是,信号直到现在才变高。

在线程中,这变成:“一个线程需要一个事件发生才能继续。如果它在现在之前发生,它可以继续;否则它会阻塞自己并等待它。

于 2019-04-19T10:12:13.400 回答