开发者

How to call async method twice simultaneously?

开发者 https://www.devze.com 2023-04-01 00:26 出处:网络
Here is the thing. I called async method with parameters. So if i had only one example of the class, that using this method it works fine.

Here is the thing. I called async method with parameters. So if i had only one example of the class, that using this method it works fine. But if I got 2 examples of my class and they both calls this async method, but with different parameters, results of one returns faster and rized handler of other example of class.

I show a little example:

public class ClassExample
{
   public ClassExample(int someParameter)
   {
      GetAsyncMethodCompleted += ClassExampleGetAsy开发者_C百科ncMethodCompleted;
      GetAsyncMethod(someParameter);
   }
   void ClassExampleGetAsyncMethodCompleted (object sender, Args e)
   {
      GetAsyncMethodCompleted -= ClassExampleGetAsyncMethodCompleted;
   }
}

So it's pretty obviously that this line

 GetAsyncMethodCompleted -= ClassExampleGetAsyncMethodCompleted;

executed at the wrong time in this case:

ClassExample(1);
ClassExample(2);

I really get why it's happening. So I need to get how can I make it works in the most elegant way.


If you can change the class implementing async method then the most elegant way would be not to use Event-based asynchronous pattern. If you can't change the class, but you are allowed to pass some user-state to the async method it could be used to find out if you should handle the event, e.g.:


public class ClassExample
{
   private object asyncCallToken = new object();

   public ClassExample(int someParameter)
   {
      GetAsyncMethodCompleted += ClassExampleGetAsyncMethodCompleted;
      GetAsyncMethod(someParameter, asyncCallToken);
   }
   void ClassExampleGetAsyncMethodCompleted (object sender, Args e)
   {
      if (e.UserState != asyncCallToken)
      {
          // the event was triggered by somebody's other call.
          return;
      }

      GetAsyncMethodCompleted -= ClassExampleGetAsyncMethodCompleted;
   }
}

Otherwise, I guess, there is no way to distinguish between events.


If you can change your code, instead of using a shared event, use a delegate parameter.

public void AsyncExecute(int parameter, EventHandler completed)
{
    ...
}


//in your code

AsyncExecute(1, delegate (object sender, EventArgs e) { code for case 1... });
AsyncExecute(2, delegate (object sender, EventArgs e) { code for case 2... });

WARNING: Adding and removing delegates from events handlers is not thread safe. Since event handlers are linked lists, and since they are not exactly synchronized, adding and removing events from multiple threads can result in unexpected results.

To do that I usually use two static functions i wrote. It is not useful in your case, that can be solved only saving somewhere the state of what delegate to call, but it can be useful in other cases:

public static class SPInterlocked
{
    public const int SpinWaitYieldThreshold = 10;

    /// <summary>
    /// Mantain a thread in wait state for a cycle.
    /// spinCounter must be a reference to a local integer variable initialized to zero.
    /// </summary>
    public static void SpinOnce(ref int spinCounter)
    {
        if (spinCounter > SpinWaitYieldThreshold || ProcessorCount <= 1)
        {
            int num = spinCounter >= SpinWaitYieldThreshold ? spinCounter - SpinWaitYieldThreshold : spinCounter;
            Thread.Sleep(num % 20 == 19 ? 1 : 0);
        }
        else
        {
            Thread.SpinWait(2 << spinCounter);
        }

        spinCounter = spinCounter == IntegerMaxValue ? SpinWaitYieldThreshold : spinCounter + 1;
    }

    /// <summary>Add an event handler as an atomic operation.</summary>
    /// <returns>True if value is not null; False if null.</returns>
    public static void AddHandler<EVENTHANDLER>(ref EVENTHANDLER handler, EVENTHANDLER value)
        where EVENTHANDLER : class
    {
        Delegate dvalue = value as Delegate;
        if (dvalue == null)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            throw new ArgumentException("Specified value is not a delegate", "value");
        }

        EVENTHANDLER temp;
        EVENTHANDLER current = handler;
        for (int spinner = 0; ; )
        {
            temp = current;
            EVENTHANDLER combined = Delegate.Combine(temp as Delegate, dvalue) as EVENTHANDLER;
            current = Interlocked.CompareExchange(ref handler, combined, temp);
            if (current == temp)
                break;
            SpinOnce(ref spinner);
        }
        while (current != temp) ;
    }

    /// <summary>Remove an event handler as an atomic operation.</summary>
    /// <returns>True if operation was performed</returns>
    public static bool RemoveHandler<EVENTHANDLER>(ref EVENTHANDLER handler, EVENTHANDLER value)
        where EVENTHANDLER : class
    {
        Delegate dvalue = value as Delegate;
        if (dvalue != null)
        {
            EVENTHANDLER temp;
            EVENTHANDLER current = handler;
            for (int spinner = 0; ; )
            {
                temp = current;
                EVENTHANDLER combined = Delegate.Remove(temp as Delegate, dvalue) as EVENTHANDLER;
                current = Interlocked.CompareExchange(ref handler, combined, temp);
                if (current == temp)
                    break;
                SpinOnce(ref spinner);
            }
            return true;
        }
        return false;
    }
}

// Your code

public static class MyClass
{
    private EventHandler eMyEvent;

    public event EventHandler MyEvent
    {
        add { SPinterlocked<EventHandler>.AddHandler(ref this.eMyEvent, value); }
        remove { SPinterlocked<EventHandler>.RemoveHandler(ref this.eMyEvent, value); }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号