[C#] Параллельное программирование на практике: подробное объяснение асинхронного программирования

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

        Эта глава начнется с введения в различия между синхронным и асинхронным кодом, затем будет обсуждаться, когда асинхронный код уместен, а когда его следует избегать. Наконец, мы обсудим новые возможности параллельного программирования, которые помогут справиться со сложностями асинхронного кода.

        Это руководство соответствует учебному проекту: Magician Dix / HandsOnParallelProgramming · GitCode.

        Эта глава эквивалентна поэтапному изложению, в ней не так уж много новых знаний и, как правило, она относительно проста.


1. Тип исполнения программы

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

        Пропустите здесь и перейдите к следующей части.

2. Ситуации, подходящие для использования асинхронного программирования

        Когда уместно использовать асинхронное программирование?У Microsoft есть документ:

Синхронные и асинхронные операции — WCF | Microsoft Learn Узнайте о реализации и вызове асинхронных операций службы. Службы и клиенты WCF могут использовать асинхронные операции на обоих уровнях приложения. icon-default.png?t=N6B9https://learn.microsoft.com/zh-cn/dotnet/framework/wcf/synchronous-and-asynchronous-operations

        Предлагаются следующие сценарии:

  • Если операция вызывается из приложения среднего уровня .

  • Если вы вызываете операцию на странице ASP.NET, вы можете использовать асинхронную страницу.

  • Если операция вызывается из любого однопоточного приложения, такого как Windows Forms или Windows Presentation Foundation (WPF). При использовании модели асинхронного вызова на основе событий результирующее событие вызывается в потоке пользовательского интерфейса, что повышает скорость реагирования приложения, не требуя от вас самостоятельно обрабатывать несколько потоков.

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

3. Напишите асинхронный код

        Существуют следующие способы асинхронной реализации:

  • Используйте метод Delegate.BeginInvoke;

  • Используйте класс Task;

  • Используйте интерфейс IAsyncResult;

  • Используйте ключевые слова async и await.

3.1. Используйте метод Delegate.BeginInvoke.

        Это метод делегирования. Давайте посмотрим непосредственно на фрагмент кода:

        private void RunWithBegionInvoke()
        {
            Action logAction = AsyncLogAction;
            logAction.BeginInvoke(null, null);
            Debug.Log("RunWithBegionInvoke !!!");
        }
        
        public static async void AsyncLogAction()
        {
            await Task.Delay(2500);
            Debug.Log("AsyncLogAction Finish !!!");
        }

        Мы выполняем его напрямую, и результаты следующие:

         Видно, что BeginInvoke не блокирует основной поток, а обеспечивает асинхронный эффект. В то же время эта функция также поддерживает обратные вызовы и параметры, которые, можно сказать, очень просты в использовании. Этот нижний уровень фактически реализуется с использованием IAsyncResult. Конечно, автоматическая реализация будет иметь определенные дополнительные накладные расходы.

3.2. Использование класса задач

        Раньше это слишком часто использовалось, поэтому я не буду вдаваться в подробности.Вы можете обратиться к главе 2. Пропускать.

3.3. Использование интерфейса IAsyncResult

Интерфейс IAsyncResult (система) | Microsoft Learn Представляет состояние асинхронной операции. icon-default.png?t=N6B9https://learn.microsoft.com/zh-cn/dotnet/api/system.iasyncresult?view=netstandard-2.1         Знание синтаксиса интерфейса такого рода легче понять, если вы непосредственно его кодируете. Здесь мы можем случайно определить наш собственный IAsyncResult, и, конечно, я могу случайно записать внутрь параметры:

    /// <summary>
    /// IAsyncResult 的示例代码;
    /// </summary>
    public class MyAsyncResult : IAsyncResult
    {
        public MyAsyncResult(MyAsyncState state)
        {
            m_AutoResetEvent = new AutoResetEvent(false);
            IsCompleted = false;
            m_AsyncState = state;
            CompletedSynchronously = false;
        }

        private MyAsyncState m_AsyncState;
        public object AsyncState => m_AsyncState;

        private AutoResetEvent m_AutoResetEvent;
        public WaitHandle AsyncWaitHandle => m_AutoResetEvent;

        public bool CompletedSynchronously { get; private set; }
        public bool IsCompleted { get; private set; }

    }
    
    /// <summary>
    ///  自定义的数据结构
    /// </summary>
    public class MyAsyncState
    {

        public Vector3 Positon;
        public Quaternion Rotation;

    }

        Затем мы пытаемся позвонить ему:

        private void RunWtihMyAsyncResult()
        {
            MyAsyncState state = new MyAsyncState();
            state.Positon = new Vector3(50, 811, 55);
            state.Rotation = Quaternion.Euler(7, 8, 9);
            MyAsyncResult result = new MyAsyncResult(state);

            Action logAction = AsyncLogAction;
            // OnActionCallBack 的回调仍然在子线程
            logAction.BeginInvoke(OnActionCallBack, result);

            Debug.Log($"RunWtihMyAsyncResult : {Task.CurrentId}");
        }
        
        public static void OnActionCallBack(IAsyncResult result)
        {
            Debug.Log($"OnActionCallBack : {result.GetType()} | {Task.CurrentId} !");

            MyAsyncResult myResult = result.AsyncState as MyAsyncResult;
            MyAsyncState myState = myResult.AsyncState as MyAsyncState;

            Debug.Log($"Positon : {myState.Positon}");
            Debug.Log($"EulerAngles : {myState.Rotation.eulerAngles}");
            Debug.Log($"IsCompleted : {result.IsCompleted}");
            Debug.Log($"CompletedSynchronously : {result.CompletedSynchronously}");
            Debug.Log($"OnActionCallBack : End  !");
        }

        Как видите, обратный вызов MyAsyncResult здесь представляет собой AsyncState в возвращаемом IAsyncResult, который похож на матрешку. Реализация интерфейса, возвращаемая системой, — это класс System.Runtime.Remoting.Messaging.AsyncResult, который должен быть автоматически инкапсулирован.

        Примечание. Интерфейс IAsyncResult, вызываемый BeginInvoke, все еще находится в дочернем потоке!

        Конечно, описанный выше метод записи обнаружил, что написанный нами самостоятельно IAsyncResult практически бесполезен... Однако C# предоставляет общий и простой метод записи:

        private void RunWtihAsyncCallBack()
        {
            MyAsyncState state = new MyAsyncState();
            state.Positon = new Vector3(888, 831, 255);
            state.Rotation = Quaternion.Euler(76, 38,329);
            Action logAction = TestFunction.AsyncLogAction;
            logAction.BeginInvoke(TestFunction.OnActionCallBackSimple, state);
        }

        Здесь также возвращается System.Runtime.Remoting.Messaging.AsyncResult. Отличие от того, что я ожидал, заключается в том, что обратный вызов выполняется до делегата, поэтому я не знаю, какое использование этого интерфейса на данный момент.

4. Ситуации, когда нецелесообразно использовать асинхронное программирование

        Здесь автор предлагает 4 сценария:

  • В одной базе данных без пула соединений

  • Уделяйте больше внимания простоте чтения и сопровождения кода.

  • Простое управление и короткое время работы

  • Приложение использует много общих ресурсов

        На самом деле для нашей Untiy разработки это не проблемы. Изначально Unity рекомендует делать все в основном потоке, а многопоточность — это расширенное использование. Программисты, пишущие многопоточность, обычно знают, когда ее использовать. Поскольку Unity отключает вызов многих интерфейсов в подпотоках, фактически большая часть кода, связанного с Unity, может использоваться только в основном потоке.Большинство используемых подпотоков представляют собой ввод-вывод или чистую обработку данных.


5. Разделы настоящей главы

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

        Это руководство соответствует учебному проекту: Magician Dix / HandsOnParallelProgramming · GitCode.

Supongo que te gusta

Origin blog.csdn.net/cyf649669121/article/details/131829094
Recomendado
Clasificación