代码之家  ›  专栏  ›  技术社区  ›  Lawrence Barsanti

PostMessage偶尔会丢失一条消息

  •  0
  • Lawrence Barsanti  · 技术社区  · 16 年前

    我编写了一个多线程windows应用程序,其中线程:

    B偶尔会生成数据并将其传递给两个A。

    线程安全队列用于将数据从线程B传递到线程A。使用windows critical section对象保护入队和出队函数。

    这在相当长的一段时间内都很有效,直到一台特定的计算机开始丢失偶尔的信息。lose的意思是,PostMessage在B中成功返回,但A从未收到消息。这会导致软件显示为冻结状态。

    我已经想出了一些可以接受的解决办法。我很想知道为什么windows会丢失这些消息,为什么这只发生在一台计算机上。

    下面是代码的相关部分。

    // Only called by B
    procedure TSharedQueue.Enqueue(AItem: TSQItem);
    var
     B: boolean;
    begin
      EnterCriticalSection(FQueueLock);
      if FCount > 0 then
        begin
          FLast.FNext := AItem;
          FLast := AItem;
        end
      else
        begin
          FFirst := AItem;
          FLast := AItem;
        end;
    
      if (FCount = 0) or (FCount mod 10 = 0) then // just in case a message is lost
        repeat
          B := PostMessage(FConsumer, SQ_HAS_DATA, 0, 0);
          if not B then 
      Sleep(1000); // this line of code has never been reached
        until B;
    
      Inc(FCount);
      LeaveCriticalSection(FQueueLock);
    end;
    
    // Only called by A 
    function TSharedQueue.Dequeue: TSQItem;
    begin
      EnterCriticalSection(FQueueLock);
      if FCount > 0 then
        begin
          Result := FFirst;
          FFirst := FFirst.FNext;
          Result.FNext := nil;
          Dec(FCount);
        end
      else
        Result := nil;
      LeaveCriticalSection(FQueueLock);
    end;
    
    // procedure called when SQ_HAS_DATA is received
    procedure TfrmMonitor.SQHasData(var AMessage: TMessage);
    var
      Item: TSQItem;
    begin
      while FMessageQueue.Count > 0 do
        begin
          Item := FMessageQueue.Dequeue;
          // use the Item somehow
        end;
    end;
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Ates Goral    16 年前

    FCount 也受 FQueueLock ? 如果不是,那么你的问题在于 在已处理已发布的邮件后递增。

    1. B进入临界段
    2. B电话 PostMessage
    3. F计数 0
    4. B增量 F计数
    5. B叶临界截面

    一个快速的补救办法是增加 F计数 打电话之前 .

    请记住,事情发生的速度可能比预期的要快(即,在您有机会在几行之后增加FCount之前,用PostMessage发布的消息会被另一个线程捕获和处理),特别是当您处于真正的多线程环境(多个CPU)中时。这就是为什么我早些时候问“问题机器”是否有多个CPU/内核。

    另一方面,在这样的生产者/消费者场景中可以做的一个很好的优化是使用两个队列而不是一个队列。当使用者醒来处理完整队列时,您可以将完整队列与空队列交换,只需锁定/处理完整队列,而新的空队列可以在两个线程不尝试锁定对方队列的情况下填充。不过,在交换这两个队列时仍然需要一些锁定。

        2
  •  1
  •   Ates Goral    16 年前

    当排队时,如果队列为空 使用PostMessage告诉一个用户

    在检查队列大小和发出消息之前是否锁定了消息队列 PostMessage ? 您可能正在经历一种竞争条件,即检查队列并发现它不是空的,而实际上a正在处理最后一条消息,并且即将空闲。

    看看你是否真的遇到了比赛情况,而不是比赛中的问题 发送消息

    这在相当长的一段时间内效果良好 丢失偶尔的信息。

    顺便问一下,这台特定计算机的CPU或内核数量是否与您认为没有问题的其他计算机不同?有时,当您从单CPU机器切换到具有多个物理CPU/内核的机器时,可能会出现新的竞争条件或死锁。

        3
  •  -1
  •   scottm    16 年前