Silverlight, dealing with Async calls

后端 未结 8 1225
走了就别回头了
走了就别回头了 2021-01-03 01:23

I have some code as below:

        foreach (var position in mAllPositions)
        {
                 DoAsyncCall(position);
        }
//I want to execute co         


        
相关标签:
8条回答
  • 2021-01-03 01:45

    Your question is a little unclear (should it be count <=?), but here goes...

    What you are asking for is how to do a synchronous call. With an async call, before you make the call you assign an event handler to be called upon completion of the call, then you make the call. This means your completion code is in a different function to the code that makes the call. This means that if your async call is made on the UI thread, the UI will not block while the call is made.

    Sync calls are possible in Silverlight, but you have to make sure you do not do them on the UI thread. One way to achieve this is to start a new background thread, execute the async call on the background thread, but block the return until the call has been completed. Here is a pseudo code sample:

    private AutoResetEvent myResetEvent;
    
    private void MyCallFunction(object someParameter) {
    
        if (this.Dispatcher.CheckAccess())
        {
            Action<object> a = new Action<object>(MyCallFunction);
            a.BeginInvoke(someParameter, null, null);
            return;
        }
    
        myResetEvent = new AutoresetEvent();
        myAsyncCall.CallCompleted += new EventHandler<>(myAsyncCall_CallCompleted);
        myAsyncCall.DoAsyncCall(someParameter);
    
        myResetEvent.WaitOne();
        //increment your count here
    }
    
    private void myAsyncCall_CallCompleted(object sender, SomeEventArgs e) {
        if (e.Error == null && !e.Cancelled) {
            if (myResetEvent != null)
                myResetEvent.Set();
        }
    }
    

    Note that this code is not particularly thread safe or production ready - it is just a quick sample.

    What happens here is that when you enter MyCallFunction, it checks to see if it is running on the UI thread, if it is then it re-invokes itself on a background thread. It then sets up an AutoResetEvent, and makes the async call. It then pauses on the myResetEvent object until it has been set (or 'signalled') from the call completed handler, at which point code execution continues. Note that you shouldn't attempt to access a control directly from code like this without first ensuring you are back on the UI thread.

    When i started working out how to do this in Silverlight, i started with this SO post, and continued with this link. But as Marc gravell says in that first post, don't do it if you can avoid it. The only time i have needed to do this was when i needed to aggregate the results of several disparate WCF calls into one result that was returned to the UI (and these calls could not be combined in to one facade style WCF method).

    0 讨论(0)
  • 2021-01-03 01:46

    You can do great things by following the concept of Async workflows (navigate to the C# paragraph) as depicted via Tomas' blog.

    http://tomasp.net/blog/csharp-async.aspx

    0 讨论(0)
  • 2021-01-03 01:47

    Given that you're using Silverlight, and you don't have access to Semaphores, you might be looking for something like this (written in notepad, so no promises as to perfect syntax):

    int completedCallCount = 0;
    int targetCount = mAllPositions.Count;
    
    using (ManualResetEvent manualResetEvent = new ManualResetEvent(false))
    {
        proxy.DoAsyncCallCompleted += (s, e) =>
        {
            if (Interlocked.Increment(ref completedCallCount) == targetCount)
            {
                manualResetEvent.Set();
            }
        };
    
        foreach (var position in mAllPositions)
        {
            proxy.DoAsyncCall(position);
        }
    
        // This will wait until all the events have completed.
        manualResetEvent.WaitOne();
    }
    

    However, one of the benefits of Silverlight forcing you to asynchronously load data is that you have to work hard to lock the UI up when making a service calls, which is exactly what you're going to do with code like this, unless you have it running on a background thread, which I strongly recommend you do. You can always display a "please wait" message until the background process completes.

    0 讨论(0)
  • 2021-01-03 01:52

    (NB, I am giving you two separate answers. This one sticks as close to your question as possible.)

    Your question says

    while{ count >= mAllPositions.Count )
    {
        //Run my code here
    }
    

    but I am guessing that what you really mean is:

    while( count < mAllPositions.Count )
       ; // do nothing -- busy wait until the count has been incremented enough
    // Now run my code here
    

    ??

    If so, then you can accomplish the same thing more efficiently by using a semaphore:

    Semaphore TheSemaphore = new Semaphore( 0, mAllPositions.Count );
    

    As each async call completes, release the semaphore.

    TheSemaphore.Release();
    

    Before executing your final code, ensure the semaphore has been released the requisite number of times:

     for( int i=0; i<mAllPositions.Count; i++ )
        TheSemaphore.WaitOne();
     // Now run follow-on code.
    

    Your code will block until the async operations have all been completed.

    0 讨论(0)
  • 2021-01-03 01:57

    Another option is to increase a counter in the foreach loop that calls the async processes. Then in the callback check you will decrement the counter and check for equality to zero. When the counter has returned to zero all calls are complete and your next code can run. Just be sure that you change the counter in a thread safe manner. The easiest way to do this might be to return to the UI thread before you decrement and test the counter.

    0 讨论(0)
  • 2021-01-03 02:02
    1. Setup a static autoResetEvent
    2. Setup ThreadPoolQueueUserWorkItems
    3. ForEach item in the loop, queue the item, and pass in the autoResetEvent as the callback's parameter.
    4. Within the callback, do the work and then call the autoresetevent.Set();
    5. In the mainbody at the point of //I want to execute code here after each Async call has finished call the appropriate autoResetEvent.Wait();
    0 讨论(0)
提交回复
热议问题