none
在使用ManualResetEvent后HttpWebRequest 不能异步响应 RRS feed

  • 问题

  • 我用HttpWebRequest  去登录一个服务器,并会返回一个xml文件,但是执行了allDone.WaitOne()后RespCallback就不会执行,把“allDone.WaitOne()”这段代码注释掉RespCallback就会执行。

    namespace HamiBook
    {
        public class RequestState
        {
            public HttpWebRequest request { get; set; }
            public HttpWebResponse response { get; set; }
            public Stream streamResponse { get; set; }
            public byte[] BufferRead { get; set; }
            public string requestData { get; set; }
        }
        public class Http
        {

            private static ManualResetEvent allDone;
            private static int BUFFER_SIZE;

            public Http()
            {
                BUFFER_SIZE = 10240;
                allDone = new ManualResetEvent(false);
            }
            public int Login()
            {
                String strLogin = String.Format("{0}?msisdn={1}&password={2}&uuid={3}&isMessage={4}&device={5}&email={6}&rmo={7}",
                   "http://booksvr.emome.net/users/login", 
                   "hamibook99@gmail.com",
                   "70476915", 
                   "A02D0DA1-80A0-4E73-B61F-793263C029EE",
                   0,
                   "w",
                   "hamibook99@gmail.com",
                   0
                   );
                try
                {
                    System.Uri uri = new Uri(strLogin);
                    // Create a HttpWebrequest object to the desired URL.
                    HttpWebRequest myHttpWebRequest1 = (HttpWebRequest)WebRequest.Create(uri);

                    // Create an instance of the RequestState and assign the previous myHttpWebRequest1
                    // object to it's request field. 
                    RequestState myRequestState = new RequestState();
                    myRequestState.BufferRead = new byte[BUFFER_SIZE];
                    myRequestState.request = myHttpWebRequest1;


                    // Start the asynchronous request.
                    IAsyncResult result =
                      (IAsyncResult)myHttpWebRequest1.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);

                    allDone.WaitOne();

                    myRequestState.response.Close();

                }
                catch (WebException e)
                {
                    string strError = "\nException raised!\n";
                    strError += "Message: ";
                    strError += e.Message;
                    strError += "\nStatus: ";
                    strError += e.Status;
                    strError += "\n";

                }
                catch (Exception e)
                {
                    string strError = "\nException raised!\n";
                    strError += "\nMessage: ";
                    strError += e.Message;
                    strError += "\n";

                }
                return 0;
            }
             private static void RespCallback(IAsyncResult asynchronousResult)
            {
                try
                {
                    // State of request is asynchronous.
                    RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;
                    HttpWebRequest myHttpWebRequest2 = myRequestState.request;
                    myRequestState.response = (HttpWebResponse)myHttpWebRequest2.EndGetResponse(asynchronousResult);

                    // Read the response into a Stream object.
                    Stream responseStream = myRequestState.response.GetResponseStream();
                    myRequestState.streamResponse = responseStream;

                    // Begin the Reading of the contents of the HTML page and print it to the console.
                    IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
                }
                catch (WebException e)
                {
                    // Need to handle the exception
                }
            }
            private static void ReadCallBack(IAsyncResult asyncResult)
            {
                try
                {

                    RequestState myRequestState = (RequestState)asyncResult.AsyncState;
                    Stream responseStream = myRequestState.streamResponse;
                    int read = responseStream.EndRead(asyncResult);
                    // Read the HTML page and then do something with it
                    if (read > 0)
                    {
                        myRequestState.requestData += Encoding.UTF8.GetString(myRequestState.BufferRead, 0, read);
                        IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
                    }
                    else
                    {
                        if (myRequestState.requestData.Length > 1)
                        {
                            string stringContent;
                            stringContent = myRequestState.requestData.ToString();
                            // do something with the response stream here
                        }

                        responseStream.Close();
                        allDone.Set() ;

                    }

                }
                catch (WebException e)
                {
                    // Need to handle the exception
                }

            }


        }
    }

    2010年11月4日 7:17

全部回复

  • 我也是一样的问题,怎么等待回调结束再执行下面的代码
    2010年11月23日 10:50
  • ManualResetEvent 使线程可以通过发信号来互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。

    当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 方法以将 ManualResetEvent 置于非终止状态。此线程可被视为控制 ManualResetEvent。对 ManualResetEvent 调用 WaitOne 方法的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 方法以发出等待线程可以继续进行的信号。并释放所有等待线程。

    一旦它被终止,ManualResetEvent 将保持终止状态,直到它被手动重置。即对 WaitOne 的调用将立即返回。

    可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态:如果初始状态处于终止状态,为 true;否则为 false


    Cedar
    2011年3月22日 14:31
  • 请把ManualResetEvent 换成AutoResetEvent 尝试。

    private static AutoResetEvent allDone = new AutoResetEvent(false);


    Cedar
    2011年3月22日 14:32
  • 下面的代码示例演示如何结合使用 ManualResetEventAutoResetEvent 同步多个线程(包括 UI 线程)的活动。

    在步骤 2 和步骤 5b 中,ManualResetEvent 用于同时释放几个线程。

    link: http://msdn.microsoft.com/zh-cn/library/system.threading.manualresetevent(v=VS.95).aspx

    示例

    using System;
    using System.Threading;

    // The following Imports are not required for ManualResetEvent or
    // AutoResetEvent; they merely simplify the code.
    using System.Windows.Controls;
    using System.Windows.Input;

    public class Example
    {
    // mre is used to block and release threads manually.
    private static ManualResetEvent mre = new ManualResetEvent(false);

    // The DemoThread method waits on this AutoResetEvent before each step of the
    // demo. The MouseLeftButtonUp event handler calls Set to allow each step to
    // run.
    private static AutoResetEvent areSyncDemoThread = new AutoResetEvent(false);

    // All output is displayed here.
    private static TextBlock outputBlock;

    // This array of AutoResetEvent objects is used to ensure that all the threads
    // created for the first step of the demo are waiting on the ManualResetEvent
    // before the step is executed, and to ensure that all the threads are complete
    // before going on to the next step.
    private static AutoResetEvent[] autoResets = { new AutoResetEvent(false),
    new AutoResetEvent(false), new AutoResetEvent(false)};


    // The static Demo method starts the thread that controls the demo and hooks
    // up the handler for the MouseLeftButtonUp event.
    public static void Demo(TextBlock outputBlock)
    {
    Example.outputBlock = outputBlock;
    outputBlock.Text += "Click here to begin the demo.\n\n";

    Thread t = new Thread(DemoThread);
    t.Start();

    outputBlock.MouseLeftButtonUp += new MouseButtonEventHandler(MouseUp);
    }


    // Each time the TextBlock is clicked, the mouse event handler calls Set() on
    // the AutoResetEvent, to signal DemoThread to execute the next step of the
    // demo. Optionally, it clears the TextBlock.
    //
    private static bool clear = false;
    private static void MouseUp(object sender, MouseButtonEventArgs e)
    {
    // If the clear flag has been set, clear the contents of the TextBlock.
    if (clear)
    {
    outputBlock.Text = "";
    clear = false;
    }
    //outputBlock.Text += "Click.\n"

    // Signal the next step of the demo to proceed.
    areSyncDemoThread.Set();
    }


    // Before each step of the demo, DemoThread waits on areSyncDemoThread. When
    // the MouseLeftButtonUp event handler signals areSyncDemoThread, DemoThread
    // executes the step. Because areSyncDemoThread is an AutoResetEvent, it
    // immediately resets after DemoThread is released.
    private static void DemoThread()
    {
    // Wait for a mouse click.
    areSyncDemoThread.WaitOne();


    // Step 1: Start 3 named threads that block on a ManualResetEvent.

    for(int i = 0; i < 3; i++)
    {
    Thread t = new Thread(ThreadProc);
    t.Name = "Thread_" + i;
    t.Start(autoResets[i]);
    }

    // Wait until all three threads have finished displaying their "start"
    // messages and called mre.WaitOne().
    WaitHandle.WaitAll(autoResets);

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\n3 threads are queued, waiting for the ManualResetEvent. Click to signal\n" +
    "the ManualResetEvent by calling its Set() method.\n\n");

    // Wait for a mouse click.
    areSyncDemoThread.WaitOne();


    // Step 2: Call mre.Set() to release the threads.

    mre.Set();

    // Wait until all three threads have finished displaying their "end"
    // messages.
    WaitHandle.WaitAll(autoResets);

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\nAll the threads were released, and the ManualResetEvent remains in the\n" +
    "signaled state. Click to start more threads.\n\n");

    // Wait for a mouse click.
    areSyncDemoThread.WaitOne();


    // Step 3: Show that mre remains signaled by starting more threads. These
    // threads will not block, so there is no reason to pass them an
    // AutoResetEvent.

    Thread[] twoMoreThreads = {new Thread(ThreadProc), new Thread(ThreadProc)};
    for(int i = 0; i < 2; i++)
    {
    twoMoreThreads[i].Name = "Thread_" + (i + 3);
    twoMoreThreads[i].Start(null);
    }

    // Wait until the threads have displayed their messages and finished
    // executing.
    foreach (Thread t in twoMoreThreads)
    {
    t.Join();
    }

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\nAs long as the ManualResetEvent remains in the signaled state, threads\n" +
    "that wait on it do not block. Click to reset the ManualResetEvent.\n");

    // Wait for a mouse click.
    areSyncDemoThread.WaitOne();


    // Step 4: Demonstrate that Reset puts the ManualResetEvent back into the
    // unsignaled state.

    outputBlock.Dispatcher.BeginInvoke(displayHelper, "\nCalling mre.Reset().\n\n");
    mre.Reset();

    // Start a thread that waits on the ManualResetEvent.
    Thread t5 = new Thread(ThreadProc);
    t5.Name = "Thread_5";
    AutoResetEvent wait = new AutoResetEvent(false);
    t5.Start(wait);

    // Wait until the thread has displayed its message and is waiting.
    wait.WaitOne();

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\nWith the ManualResetEvent in the unsignaled state, threads once again block.\n" +
    "Click to release the thread.\n\n");

    // Signal the MouseUp event to clear the screen, and wait for a click.
    clear = true;
    areSyncDemoThread.WaitOne();

    // Release the waiting thread, and block until it ends.
    mre.Set();
    t5.Join();


    // Step 5a: Signal an AutoResetEvent that does not have a thread waiting on
    // it. Note that you can create an AutoResetEvent in the signaled
    // state by using New AutoResetEvent(True).

    // Put the AutoResetEvent into the signaled state.
    wait.Set();

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\nIf an AutoResetEvent is signaled when there is no thread waiting on it, \n" +
    "the AutoResetEvent remains in the signaled state until a thread waits on\n" +
    "it. That thread is immediately released, and the AutoResetEvent returns\n" +
    "to the unsignaled state. Click here to demonstrate this.\n\n");

    // Wait for a click.
    areSyncDemoThread.WaitOne();


    // Step 5b: Create and release three threads that all wait on the signaled
    // AutoResetEvent. Each thread receives an array that contains two
    // AutoResetEvent objects, one to signal when the thread is ready
    // for release and one to wait on.

    // Reset the ManualResetEvent that will synchronize the release of the three
    // threads.
    mre.Reset();

    for(int i=0; i<=2; i++)
    {
    Thread t = new Thread(ThreadProcARE);
    t.Name = "Thread_"+ (i + 6);
    t.Start(new AutoResetEvent[]{ autoResets[i], wait });
    }

    // Wait until all three threads are queued, then release them all at once.
    WaitHandle.WaitAll(autoResets);
    mre.Set();

    // Wait until one thread has been released by the signaled AutoResetEvent and
    // has posted its message to the TextBlock.
    int winner = WaitHandle.WaitAny(autoResets);

    string name = "Thread_" + (winner + 6);
    outputBlock.Dispatcher.BeginInvoke(displayHelper, "\n" + name +
    " was the first thread to wait on the signaled AutoResetEvent. As soon\n" +
    "as " + name + " was released, the AutoResetEvent was reset, blocking the other two\n" +
    "threads. Click to release the waiting threads.\n\n");

    // Wait for a click.
    areSyncDemoThread.WaitOne();


    // Step 6: Release threads and dispose of the Shared resources.

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "Set() is called twice on the AutoResetEvent, releasing one waiting thread\n" +
    "each time. In order to ensure that both threads are released, a suitable delay\n" +
    "must elapse between calls to Set(). This is accomplished by calling WaitAny()\n" +
    "on the autoResets array.\n\n");
    wait.Set();
    WaitHandle.WaitAny(autoResets);
    wait.Set();

    // To ensure that both threads have ended before DemoThread ends, WaitAny()
    // is called to wait for the last thread to end. When DemoThread ends, the
    // variable 'wait' goes out of scope and the AutoResetEvent it holds is reclaimed
    // by garbage collection. If the thread is not released before this, it will not
    // be released (and therefore will not run) and it will not be reclaimed by
    // collection until the application ends. You might think that the end of
    // DemoThread and the end of the application are synonymous, but in fact the
    // application is still running as long as the browser window remains active.
    // The questions of whether it is important for the last thread to run before
    // the application ends, or whether it is a problem to temporarily leak the
    // thread, will have different answers from application to application.
    WaitHandle.WaitAny(autoResets);

    // Dispose of the static (class-level) wait handles. This is important only
    // if the program will go on running, and the wait handles will not be used.
    mre.Close();
    areSyncDemoThread.Close();
    foreach (AutoResetEvent are in autoResets)
    {
    are.Close();
    }

    // Unhook the mouse button event.
    outputBlock.Dispatcher.BeginInvoke(delegate () {
    outputBlock.MouseLeftButtonUp -= new MouseButtonEventHandler(MouseUp); });

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    "\nTo run the demo again, refresh the page.\n");
    }


    // Thread Procedures:

    // This thread procedure is executed by most of the named threads created in
    // this example.
    private static void ThreadProc(object state)
    {
    AutoResetEvent are = (AutoResetEvent) state;
    string name = Thread.CurrentThread.Name;

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    name + " starts and calls mre.WaitOne()\n");

    // Signal that the thread is about to wait.
    if (are != null) { are.Set(); }

    // Wait until the ManualResetEvent is signaled.
    mre.WaitOne();

    outputBlock.Dispatcher.BeginInvoke(displayHelper, name + " ends.\n");

    // Signal that the thread is about to exit.
    if (are != null) { are.Set(); }
    }


    // This thread procedure is used by the last step, which shows the behavior
    // of an AutoResetEvent that has been left in the signaled state.
    private static void ThreadProcARE(object state)
    {
    // Get two AutoResetEvent objects, one to signal when this thread is ready
    // for release, and one to wait on. The second AutoResetEvent is already
    // in the signaled state.
    AutoResetEvent[] are = (AutoResetEvent[]) state;
    string name = Thread.CurrentThread.Name;

    // Signal that this thread is ready for release.
    are[0].Set();

    // All threads are started at once, when mre is signaled.
    mre.WaitOne();

    outputBlock.Dispatcher.BeginInvoke(displayHelper,
    name + " waits on the AutoResetEvent.\n");

    // Wait on the previously signaled AutoResetEvent.
    are[1].WaitOne();

    outputBlock.Dispatcher.BeginInvoke(displayHelper, name + " ends.\n");

    // Signal that this thread is ready to end.
    are[0].Set();
    }


    // Helper methods:

    // In order to update the TextBlock object, which is on the UI thread, you must
    // make a cross-thread call by using the Dispatcher object that is associated
    // with the TextBlock. The DisplayOutput helper method and its delegate,
    // displayHelper, are used by the BeginInvoke method of the Dispatcher object
    // to append text to the TextBlock.
    //
    private static Action<string> displayHelper = new Action<string>(DisplayOutput);
    private static void DisplayOutput(string msg)
    {
    outputBlock.Text += msg;
    }
    }

    /* This example produces output similar to the following:

    Click here to begin the demo.

    Thread_0 starts and calls mre.WaitOne()
    Thread_1 starts and calls mre.WaitOne()
    Thread_2 starts and calls mre.WaitOne()

    3 threads are queued, waiting for the ManualResetEvent. Click to signal
    the ManualResetEvent by calling its Set() method.

    Thread_2 ends.
    Thread_1 ends.
    Thread_0 ends.

    All the threads were released, and the ManualResetEvent remains in the
    signaled state. Click to start more threads.

    Thread_3 starts and calls mre.WaitOne()
    Thread_3 ends.
    Thread_4 starts and calls mre.WaitOne()
    Thread_4 ends.

    As long as the ManualResetEvent remains in the signaled state, threads
    that wait on it do not block. Click to reset the ManualResetEvent.

    Calling mre.Reset()

    Thread_5 starts and calls mre.WaitOne()

    With the ManualResetEvent in the unsignaled state, threads once again block.
    Click to release the thread.

    Thread_5 ends.

    If an AutoResetEvent is signaled when there is no thread waiting on it,
    the AutoResetEvent remains in the signaled state until a thread waits on
    it. That thread is immediately released, and the AutoResetEvent returns
    to the unsignaled state. Click here to demonstrate this.

    Thread_7 waits on the AutoResetEvent.
    Thread_7 ends.
    Thread_8 waits on the AutoResetEvent.
    Thread_6 waits on the AutoResetEvent.

    Thread_7 was the first thread to wait on the signaled AutoResetEvent. As soon
    as Thread_7 was released, the AutoResetEvent was reset, blocking the other
    two threads. Click to release the waiting threads.

    Set() is called twice on the AutoResetEvent, releasing one waiting thread
    each time. In order to ensure that both threads are released, a suitable delay
    must elapse between calls to Set(). This is accomplished by calling WaitAny()
    on the autoResets array.

    Thread_8 ends.
    Thread_6 ends.

    To run the demo again, refresh the page.
    */



    Cedar
    2011年3月22日 14:33
  • 遇到相同的问题,请问解决这个问题了吗?

    能共享下解决方法吗?

    2012年8月10日 9:08
  • 楼主好:)

    我的意见是既然你使用BeginInvoke方法,那么你完全可以使用对应的EndInvoke方法直到此异步方法执行完毕为止,在“Login()”方法中可以这样做(请不必使用ManualResetEvent信号标识)。

    IAsyncResult result =
    IAsyncResult)myHttpWebRequest1.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
    myHttpWebRequest1.EndInvoke(IAsyncResult);

    如果你一定需要用,可以这样参考:

     allDone.Set() ;

    把这句话挪动到else外边(挪动到整个if……else……外边)。


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处


    2012年8月11日 3:48
    版主
  • 楼主好:)

    我的意见是既然你使用BeginInvoke方法,那么你完全可以使用对应的EndInvoke方法直到此异步方法执行完毕为止,在“Login()”方法中可以这样做(请不必使用ManualResetEvent信号标识)。

    IAsyncResult result =
    IAsyncResult)myHttpWebRequest1.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
    myHttpWebRequest1.EndInvoke(IAsyncResult);

    如果你一定需要用,可以这样参考:

     allDone.Set() ;

    把这句话挪动到else外边(挪动到整个if……else……外边)。


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处


    严重同意Volunteer的说法, 你何必去浪费那个内核对象呢, 就直接用EndGetResponse就很好(Volunteer误写成了EndInvoke)。其实你用了Begin方法就调用End去等待,还不如就用同步的方法。

    2012年8月17日 3:20