c# - run - 유니티 async await



장기 실행 작업을 async/await 패턴과 결합하는 올바른 방법은 무엇입니까? (1)

다음은 몇 가지 사항입니다.

  • async void 메소드는 비동기 이벤트 핸들러에만 유용합니다 ( 자세한 정보 ). async void ExecuteAsync() 는 즉시 반환합니다 (코드 흐름이 await _pauseSource 내부에 await _pauseSource 하는 즉시). 본질적으로 _task 는 완료된 상태가되고 ExecuteAsync 의 나머지는 관찰되지 않고 실행됩니다 ( void 이기 때문에). 메인 스레드 (그리고 프로세스)가 끝나는 시점에 따라 실행을 계속하지 못할 수도 있습니다.

  • 이 경우 async Task ExecuteAsync() 를 만들고 new Task 대신 Task.Run 또는 Task.Factory.StartNew 사용하여 new Task 을 시작해야합니다. 작업의 작업 방법을 async 로 만들려면 여기에 중첩 된 작업 (예 : Task<Task> )을 Task.Run 합니다. Task.Run 은 자동 Task.Run Task<Task> 을 수행합니다. 더 많은 정보는 herehere 에서 찾을 수 있습니다.

  • PauseTokenSource 는 다음과 같은 접근 방식을 취합니다 (AFAIU). 코드의 소비자 측 ( Pause 를 호출하는 측)은 실제로 Pause 만 요청하지만 동기화는하지 않습니다. 제작자 측이 아직 대기 상태에 이르지 못한 경우 await _pauseSource.Token.WaitWhilePausedAsync()Pause 후에도 계속 실행됩니다. 앱 논리는 괜찮을 지 모르지만주의해야합니다. 더 많은 정보는 here .

[업데이트] 아래는 Factory.StartNew 를 사용하기위한 올바른 구문입니다. 참고 Task<Task>Task<Task> . task.Unwrap . 또한 Stop 에서 _task.Wait() 를주의하십시오. Stop 리턴 할 때 ( Thread.Join 과 비슷한 방식으로) 작업이 완료되었는지 확인해야합니다. 또한 TaskScheduler.DefaultFactory.StartNew 에게 스레드 풀 스케줄러를 사용하도록 지시하는 데 사용됩니다. 이것은 다른 태스크 내부에서 HighPrecisionTimer 객체를 생성하는 경우 중요합니다.이 객체는 기본 스레드가 아닌 기본 동기화 컨텍스트 (예 : UI 스레드) ( here 및 here 자세히 설명 here )에서 생성되었습니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    public class HighPrecisionTimer
    {
        Task _task;
        CancellationTokenSource _cancelSource;

        public void Start()
        {
            _cancelSource = new CancellationTokenSource();

            Task<Task> task = Task.Factory.StartNew(
                function: ExecuteAsync, 
                cancellationToken: _cancelSource.Token, 
                creationOptions: TaskCreationOptions.LongRunning, 
                scheduler: TaskScheduler.Default);

            _task = task.Unwrap();
        }

        public void Stop()
        {
            _cancelSource.Cancel(); // request the cancellation

            _task.Wait(); // wait for the task to complete
        }

        async Task ExecuteAsync()
        {
            Console.WriteLine("Enter ExecuteAsync");
            while (!_cancelSource.IsCancellationRequested)
            {
                await Task.Delay(42); // for testing

                // DO CUSTOM TIMER STUFF...
            }
            Console.WriteLine("Exit ExecuteAsync");
        }
    }

    class Program
    {
        public static void Main()
        {
            var highPrecisionTimer = new HighPrecisionTimer();

            Console.WriteLine("Start timer");
            highPrecisionTimer.Start();

            Thread.Sleep(2000);

            Console.WriteLine("Stop timer");
            highPrecisionTimer.Stop();

            Console.WriteLine("Press Enter to exit...");
            Console.ReadLine();
        }
    }
}

나는 "고 정밀도"타이머 클래스를 가지고 시작, 중지 및 일시 정지 / 재개가 가능해야합니다. 이렇게하려면 인터넷에서 찾은 몇 가지 다른 예제를 함께 묶어 놓겠습니다.하지만 asnyc를 사용하여 작업을 사용하고 있는지 / 제대로 알고 있는지 확실하지 않습니다.

내 관련 코드는 다음과 같습니다.

//based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/
public class HighPrecisionTimer : IDisposable
{
    Task _task;
    CancellationTokenSource _cancelSource;

    //based on http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
    PauseTokenSource _pauseSource;

    Stopwatch _watch;
    Stopwatch Watch { get { return _watch ?? (_watch = Stopwatch.StartNew()); } }

    public bool IsPaused
    {
        get { return _pauseSource != null && _pauseSource.IsPaused; }
        private set
        {
            if (value)
            {
                _pauseSource = new PauseTokenSource();
            }
            else
            {
                _pauseSource.IsPaused = false;
            }
        }
    }

    public bool IsRunning { get { return !IsPaused && _task != null && _task.Status == TaskStatus.Running; } }

    public void Start()
    {
        if (IsPaused)
        {
            IsPaused = false;
        }
        else if (!IsRunning)
        {
            _cancelSource = new CancellationTokenSource();
            _task = new Task(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning);
            _task.Start();
        }
    }

    public void Stop()
    {
        if (_cancelSource != null)
        {
            _cancelSource.Cancel();
        }
    }

    public void Pause()
    {
        if (!IsPaused)
        {
            if (_watch != null)
            {
                _watch.Stop();
            }
        }

        IsPaused = !IsPaused;
    }

    async void ExecuteAsync()
    {
        while (!_cancelSource.IsCancellationRequested)
        {
            if (_pauseSource != null && _pauseSource.IsPaused)
            {
                await _pauseSource.Token.WaitWhilePausedAsync();
            }

            // DO CUSTOM TIMER STUFF...
        }

        if (_watch != null)
        {
            _watch.Stop();
            _watch = null;
        }

        _cancelSource = null;
        _pauseSource = null;
    }

    public void Dispose()
    {
        if (IsRunning)
        {
            _cancelSource.Cancel();
        }
    }
}

누구든지 좀 봐 주시겠습니까 나 에게이 포인터를 올바르게 제공하고 있는지 여부를 제공합니까?

최신 정보

아래에서 Noseratio의 의견에 따라 코드를 수정하려고 시도했지만 구문을 파악할 수 없습니다. ExecuteAsync () 메서드를 TaskFactory.StartNew 또는 Task.Run 에 전달하려고하면 다음과 같은 컴파일 오류가 발생합니다.

"이 호출은 다음 메소드 또는 속성 사이에서 모호합니다. TaskFactory.StartNew (Action, CancellationToken ...) 및 TaskFactory.StartNew <Task> (Func <Task>, CancellationToken ...)".

마지막으로, TaskScheduler를 제공 할 필요없이 LongRunning TaskCreationOption을 지정하는 방법이 있습니까?

async **Task** ExecuteAsync()
{
    while (!_cancelSource.IsCancellationRequested)
    {
        if (_pauseSource != null && _pauseSource.IsPaused)
        {
            await _pauseSource.Token.WaitWhilePausedAsync();
        }
        //...
    }
}

public void Start()
{
    //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, null);

    //_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token);

    //_task = Task.Run(ExecuteAsync, _cancelSource.Token);

}

업데이트 2

나는 이것이 좁혀 졌다고 생각하지만, 여전히 올바른 구문에 대해서는 확실하지 않다. 이것은 작업을 생성하고 소비자 / 호출 코드가 계속해서 작업이 회전하고 새로운 비동기 스레드에서 시작되도록 올바른 방법일까요?

_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token);

//**OR**

_task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);




long-running-processes