【问题标题】:C# Threading and using AutoResetEventC# 线程和使用 AutoResetEvent
【发布时间】:2009-05-11 22:51:08
【问题描述】:

我在类库中有以下代码。我等待回电到我的主应用程序。我正在进行 DownloadStringAsync 调用,因此在完成后我必须等待几秒钟才能获得回调。我有 3 个这样的调用要等待,所以在我的主应用程序中,我使用 AutoResetEvent 来等待它们全部完成。所以我会阻塞,直到它们在回调函数中被设置。

但是,在测试回调后不会被调用。我在想,当代码被 AutoResetEvent 阻止时,它会阻止 DownloadStringAsync。当我注释掉这段代码时,一切正常。

所以我想只要我打电话给:objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber); 当代码到达这里时:handle.WaitOne(); 它会阻塞类库中的代码。

非常感谢您的建议。

在我的类库代码示例中。

     // Event handler that makes a call back in my main application
     // Event handler and method that handles the event
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
    // The method that raises the event.
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        if (NoGatewayCompletedEvent != null)
        {
            NoGatewayCompletedEvent(this, e);
        }
    }

    // Start the Async call to find if NoGateway is true or false
    public void NoGatewayStatus(string sipUsername, string phoneNumber)
    {     
        string strURL = string.Format("http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber);

        if (!wc.IsBusy)
        {
            try
            {
                string errorMsg = string.Empty;
                wc.DownloadStringAsync(new Uri(strURL));
            }
            catch (WebException ex)
            {
                Console.WriteLine("IsNoGateway: " + ex.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("IsNoGateway: " + ex.Message);
            }
        }
        else
        {
            Console.WriteLine("WebClient: IsNoGateWay(): Busy please try again");
        }

    }

    void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            if (e.Result == "No gateway")
            {
                OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.VALIDATION_FAILED));
                Console.WriteLine("NoGatway() DownloadedCompleted: " + e.Result);
            }
            else
            {
                OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.OK));
                Console.WriteLine("NoGateway() DownloadCompleted: " + e.Result);
            }
        }
        else
        {
            this.OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.SERVER_FAILED));
            Console.WriteLine("No Gateway: DownloadCompleted() Error: " + e.Error.Message);
        }
    }

在我的主应用程序中,我注册了这个回调。并等待结果。然后设置 AutoResetEvent。

 ManualResetEvent[] waitValidateCallResponse = new ManualResetEvent[] 
          { new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false) };
    // Event handler for NoGateway event
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
        waitValidateCallResponse[0].Set();
    }

我打电话和阻止的部分。

NoGateway objNoGateway = new NoGateway()           
objNoGateway.NoGatewayCompletedEvent += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);
objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber);


// Block here - Wait for all reponses to finish before moving on
waitEvent.WaitOne(5000, true);                      
Console.WriteLine("All thread finished");    

======================== 编辑并添加其他 2 个回调,以免混淆我只有一个 ==== 的问题===================

    private void OnCalledNumberBlockedCompleted(object sender, CalledNumberBlockedEventArgs e)
    {
        Console.WriteLine("OnCalledNumberBlockedCompleted: " + e.CalledNumberBlocked);
        waitValidateCallResponse[1].Set();
    }

    private void OnValidTelephoneNumberCompleted(object sender, ValidTelephoneNumberEventArgs e)
    {
        Console.WriteLine("OnValidTelephoneNumberCompleted: " + e.validTelephoneNumber);
        waitValidateCallResponse[2].Set();
    }

【问题讨论】:

  • 根据您的代码,您应该在控制台上阅读“events.WaitOne():” + handle.ToString() 一次,然后再挂起。你没有在你的帖子中谈论它,但这是一个有价值的线索。那么,发生了吗?
  • 这条线永远不会到达:Console.WriteLine("events.WaitOne(): " + handle.ToString());
  • 如果我必须调试它,我会在调试器中逐步完成。你能告诉我们哪些代码行执行,哪些不执行吗?回调是否正在执行?是否执行了 wc_DownloadStringCompleted 方法?在您的代码中,您没有显示调用 OnValidTelephoneNumberCompleted 和 OnCalledNumberBlockedCompleted 方法的位置。另外,这让我很恼火: public EventHandler NoGatewayCompletedEvent;还有更远的...... NoGatewayCompletedEvent(this, e);好吧,这不应该编译,你没有写“事件”关键字。但这可能无关紧要。
  • 我已经踩过很多次了。但是,我可以确认没有调用回调。一旦调用 waitOne() ,它就会冻结。回调应该需要更长的时间才能被调用,因为它必须从 Web 服务器接收消息。但是那个时候 waitOne 已经阻塞了。我想这可能很简单。但是,阅读别人的代码已经比较困难了。
  • 如果你已将 WaitOne 更改为 WaitAll,则无需循环

标签: c# multithreading


【解决方案1】:

是不是很简单:你总是在索引 0 上调用 Set?

private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
    Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
    waitValidateCallResponse[0].Set();
}

【讨论】:

  • 实际上我有 3 个不同的回调。为了简单起见,我在这里只展示了其中一个。其他回调将设置 1 和 2。我更改为 ManualResetEvent。然而问题依然存在。如果我注释掉这一行 handle.WaitOne();我的回电收到了。但是,当我取消注释时,它会继续阻止并冻结我的 UI。感谢您提供更多建议。
  • 我怀疑这样的东西被用来调用完成的事件。 asyncOp.PostOperationCompleted(this.onCompletedDelegate, args);当异步线程使用 PostOperationComplete 调用完成委托时,它使用调用类的线程上下文。如果在启动异步线程后在调用线程上下文中调用了 WaitOne,由于调用类线程被阻塞,它将无法执行。
【解决方案2】:

尝试以下方法:

public void NoGatewayStatus (string sipUsername, string phoneNumber) {
    string strURL = string.Format( "http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber );

    ManualResetEvent wait1 = new ManualResetEvent( false );
    WebClient wc = new WebClient();
    Thread thr = new Thread( DownloadSomeStuff );
    thr.Start( new DlArguments( strURL, wait1 ) );

    // do the other three

    if ( !wait1.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadSomeStuff timed out" );
        return;
    }
    if ( !wait2.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadOtherStuff timed out" );
        return;
    }
    if ( !wait3.WaitOne( 10000 ) ) {
        Console.WriteLine( "DownloadMoreStuff timed out" );
        return;
    }
}

public void DownloadSomeStuff (object p_args) {
    DlArguments args = (DlArguments) p_args;
    try {
        WebClient wc = new WebClient();
        wc.DownloadString( args.Url );
        args.WaitHandle.Set();
    } catch ( Exception ) {
        // boring stuff
    }
}


private class DlArguments
{
    public DlArguments (string url, ManualResetEvent wait_handle) {
        this.Url = url;
        this.WaitHandle = wait_handle;
    }

    public string Url { get; set; }
    public ManualResetEvent WaitHandle { get; set; }
}

这样可以吗?

【讨论】:

  • 我想我明白你的意思了。我会尽力让你知道。
【解决方案3】:

经过大量编辑,我想我可能会理解问题所在。 Windows 窗体应用程序有一个主线程;该线程用于处理消息。因此,当您的主线程阻塞时,您的应用程序无法接收事件。而你使用 WaitOne 来保持主线程被阻塞。

我会将 WaitOne() 检查移至单独的计时器线程。

或者您可以等待一段有限的时间,然后指示应用程序在其间处理消息:

foreach (WaitHandle handle in waitValidateCallResponse)
{
    while (!handle.WaitOne(300))
        Application.ProcessMessages();
    Console.WriteLine("events.WaitOne(): " + handle.ToString());
}

但是,后一种方法不是您应该在库中执行的操作。我认为这是一种反模式。

【讨论】:

  • 我已经添加了剩下的 2 个回调。普通的,我没有展示它们,因为它使代码太长而无法发布。
  • 这条线永远不会到达。所以永远不要写入控制台。 Console.WriteLine("events.WaitOne():" + handle.ToString());
  • OnNoGatewayCompleted 永远不会到达。在回调有机会回调之前,waitOne() 已经阻塞。
  • 我已经从 AutoResetEvent 更改为 ManualResetEvent。我已经编辑了我的代码。
  • 您好,我想您可能是对的,主 UI 线程被阻塞,因此无法接收任何消息。 Application.ProcessMessages() 直到智能才找到(我错过了什么)。对于一个完整的解决方案,我是否可以在另一个线程中处理这个问题?
【解决方案4】:

sn-p 代码奇特

// Event handler that makes a call back in my main application
     // Event handler and method that handles the event
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent;
    // The method that raises the event.
    public void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
    {
        if (NoGatewayCompletedEvent != null)
        {
            NoGatewayCompletedEvent(this, e);
        }
    }

但是,在第二个最后的 sn-p 中,您为该事件附加了一个事件处理程序,如下所示.. OnNoGatewayCompleted 似乎是引发事件的辅助方法..(它不应该是公共的)但在这里看起来你有事件处理程序再次引发事件。除非您有 2 个名为 OnNoGatewayCompleted 的方法(我希望没有)

objNoGateway.NoGatewayCompletedEvent 
  += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted);

如果您正在寻找要在事件处理程序中发出信号的 waitHandles,不应该将方法 OnCalledNumberBlockedCompleted 连接到事件。

PS:正如 Marc 指出的那样......使用WaitHandle.WaitAll(for 循环要求异步操作按顺序完成,但情况可能并非如此)

【讨论】:

  • OnCalledNumberBlockedCompleted 上钩了,只是我没有显示它,因为它使帖子更长。我刚刚演示了 NoGateway 事件处理程序。
【解决方案5】:

使用 WaitHandle.WaitAny(handleArray);等待句柄数组中的所有句柄而不是 handle.WaitOne();在一个循环中

【讨论】:

  • 它不会更难,但......将是相同的,除了没有循环
  • 但 WaitOne 比 WaitAll 更易于调试
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多