C # Threading и использование AutoResetEvent - PullRequest
2 голосов
/ 11 мая 2009

У меня есть следующий код в библиотеке классов. И я жду звонка обратно в мое основное приложение. Я делаю вызов 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();
    }

Ответы [ 5 ]

1 голос
/ 11 мая 2009

Попробуйте что-нибудь вроде этого:

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; }
}

Это делает это?

1 голос
/ 11 мая 2009

Это так просто: вы всегда вызываете Set для индекса 0?

private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e)
{
    Console.WriteLine("OnNoGatewayComleted: " + e.noGateway);
    waitValidateCallResponse[0].Set();
}
0 голосов
/ 11 мая 2009

Использовать WaitHandle.WaitAny (handleArray); ждать на всех дескрипторах в массиве handle вместо handle.WaitOne (); в петле

0 голосов
/ 11 мая 2009

Код фрагмента является своеобразным

// 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);
        }
    }

Однако во втором последнем фрагменте вы прикрепляете обработчик события для этого события следующим образом. Кажется, OnNoGatewayCompleted является вспомогательным методом для вызова события .. (оно не должно быть открытым), но здесь, похоже, у вас есть событие обработчик поднять событие снова. Если у вас нет 2 методов с именем OnNoGatewayCompleted (я надеюсь, что нет)

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

Если вы ищете, чтобы waitHandles были сигнализированы в обработчике событий, не следует ли вместо этого подключать методы OnCalledNumberBlockedCompleted к событию.

PS: Как отметил Марк .. используйте WaitHandle.WaitAll (цикл for требует завершения асинхронных операций в порядке, который может быть не тот)

0 голосов
/ 11 мая 2009

После многих правок, я думаю, я мог бы понять проблему. Приложения Windows Forms имеют один основной поток; этот поток используется для обработки сообщений. Поэтому, когда ваш основной поток блокируется, ваше приложение не может получать события. И вы используете WaitOne, чтобы блокировать основной поток.

Я бы переместил проверки WaitOne () в отдельный поток таймера.

Или вы можете подождать ограниченное время и указать приложению обрабатывать сообщения между:

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

Более поздний подход - это не то, что вы должны делать в библиотеке. Я думаю, это что-то вроде анти-паттерна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...