Как вызвать асинхронный метод дважды одновременно? - PullRequest
1 голос
/ 01 сентября 2011

Вот эта вещь.Я вызвал асинхронный метод с параметрами.Так что, если у меня был только один пример класса, этот метод работает нормально.Но если у меня есть 2 примера моего класса, и они оба вызывают этот асинхронный метод, но с разными параметрами, результаты одного возвращаются быстрее, а обработчик из другого примера класса.

Я показываю небольшой пример:

public class ClassExample
{
   public ClassExample(int someParameter)
   {
      GetAsyncMethodCompleted += ClassExampleGetAsyncMethodCompleted;
      GetAsyncMethod(someParameter);
   }
   void ClassExampleGetAsyncMethodCompleted (object sender, Args e)
   {
      GetAsyncMethodCompleted -= ClassExampleGetAsyncMethodCompleted;
   }
}

Так что вполне очевидно, что эта строка

 GetAsyncMethodCompleted -= ClassExampleGetAsyncMethodCompleted;

выполняется в неправильное время, в этом случае:

ClassExample(1);
ClassExample(2);

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

Ответы [ 2 ]

0 голосов
/ 01 сентября 2011

Если вы можете изменить свой код, вместо использования общего события, используйте параметр делегата.

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

ВНИМАНИЕ: Добавление и удаление делегатов из обработчиков событий не является потокобезопасным. Поскольку обработчики событий являются связанными списками и поскольку они не синхронизированы точно, добавление и удаление событий из нескольких потоков может привести к неожиданным результатам.

Для этого я обычно использую две статические функции, которые я написал. В вашем случае это бесполезно, это может быть решено только сохранением где-нибудь состояния вызываемого делегата, но может быть полезно в других случаях:

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 голосов
/ 01 сентября 2011

Если вы можете изменить класс, реализующий асинхронный метод, то самый элегантный способ - не использовать асинхронный шаблон на основе событий. Если вы не можете изменить класс, но вам разрешено передать какое-то пользовательское состояние асинхронному методу, его можно использовать, чтобы узнать, следует ли обрабатывать событие, например ::

.

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

В противном случае, я думаю, нет никакого способа отличить события.

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