Development Tip

비동기 작업을 어떻게 실행합니까?

yourdevel 2020. 10. 2. 23:27
반응형

비동기 작업을 어떻게 실행합니까? 방법을 동 기적으로?


async / await에 대해 배우고 있는데 비동기 메서드를 동 기적으로 호출해야하는 상황이 발생했습니다. 어떻게 할 수 있습니까?

비동기 방식 :

public async Task<Customers> GetCustomers()
{
    return await Service.GetCustomersAsync();
}

일반적인 사용법 :

public async void GetCustomers()
{
    customerList = await GetCustomers();
}

다음을 사용해 보았습니다.

Task<Customer> task = GetCustomers();
task.Wait()

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)

나는 또한 여기 에서 제안을 시도했지만 디스패처가 일시 중지 상태에 있으면 작동하지 않습니다.

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

다음은 호출의 예외 및 스택 추적입니다 RunSynchronously.

System.InvalidOperationException

메시지 : 대리인에 바인딩되지 않은 작업에서 RunSynchronously를 호출 할 수 없습니다.

InnerException : null

출처 : mscorlib

StackTrace :

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

다음은 모든 경우에 작동하는 해결 방법입니다 (일시 중단 된 디스패처 포함). 그것은 내 코드가 아니며 여전히 완전히 이해하기 위해 노력하고 있지만 작동합니다.

다음을 사용하여 호출 할 수 있습니다.

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

코드는 여기에서

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }
}

알린다 이 대답은 세 가지 살입니다. 나는 주로 .Net 4.0에 대한 경험을 기반으로 작성했으며 특히 async-await. 일반적으로 말해서 그것은 좋은 간단한 해결책이지만 때로는 일을 깨뜨립니다. 의견에서 토론을 읽으십시오.

.Net 4.5

이것을 사용하십시오 :

// For Task<T>: will block until the task is completed...
var result = task.Result; 

// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();

참조 : TaskAwaiter , Task.Result , Task.RunSynchronously


.Net 4.0

이것을 사용하십시오 :

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...아니면 이거:

task.Start();
task.Wait();

놀랍게도 아무도 이것을 언급하지 않았습니다.

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

여기에있는 다른 방법 중 일부만큼 예쁘지는 않지만 다음과 같은 이점이 있습니다.

  • 그것은 예외 (같은 삼키는하지 않습니다 Wait)
  • AggregateException(같은 Result)에 던져진 예외를 래핑하지 않습니다.
  • 모두를 위해 작동 Task하고 Task<T>( 자신을 밖으로 시도! )

또한 GetAwaiter덕 형식이므로 Tasks뿐만 아니라 비동기 메서드 ( ConfiguredAwaitable또는 등 YieldAwaitable) 에서 반환 된 모든 개체에 대해 작동합니다 .


편집 : ( 직접 호출하는 것만이 아니라) 도달 할 수있는 모든 비동기 메서드에 대해 기다릴 때마다 .Result추가하지 않는 한이 접근 방식 (또는 사용 )이 교착 상태에 빠질 .ConfigureAwait(false)수 있습니다 BlahAsync(). 설명 .

// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
                                        // all its descendants use ConfigureAwait(false)
                                        // too. Then you can be sure that
                                        // BlahAsync().GetAwaiter().GetResult()
                                        // won't deadlock.

.ConfigureAwait(false)모든 곳에 추가하기에는 너무 게으르고 성능에 관심이 없다면 대안으로 할 수 있습니다.

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()

스케줄러가 동 기적으로 실행되도록 속이는 것보다 스레드 풀에서 작업을 실행하는 것이 훨씬 간단합니다. 그렇게하면 교착 상태가되지 않는다는 것을 확신 할 수 있습니다. 컨텍스트 전환으로 인해 성능이 영향을받습니다.

Task<MyResult> DoSomethingAsync() { ... }

// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());

// Will block until the task is completed...
MyResult result = task.Result; 

async / await에 대해 배우고 있는데 비동기 메서드를 동 기적으로 호출해야하는 상황이 발생했습니다. 어떻게 할 수 있습니까?

가장 좋은 대답은 그렇지 은 "상황이"무엇에 의존 세부 사항.

속성 게터 / 세터입니까? 대부분의 경우 "비동기 속성"보다 비동기 메서드를 사용하는 것이 좋습니다. (자세한 내용 은 비동기 속성에 대한 내 블로그 게시물을 참조하십시오 ).

MVVM 앱이고 비동기 데이터 바인딩을 수행 하시겠습니까? 그런 다음 비동기 데이터 바인딩에 대한 MSDN 문서에서NotifyTask 설명한대로 my와 같은 것을 사용합니다 .

생성자입니까? 그런 다음 비동기 팩토리 메서드를 고려하고 싶을 것입니다. (자세한 내용 은 비동기 생성자에 대한 내 블로그 게시물을 참조하십시오 ).

비동기 동기화보다 거의 항상 더 나은 대답이 있습니다.

상황에 맞지 않는 경우 (여기 에서 상황을 설명 하는 질문을하여이를 알고 있다면 ) 동기 코드를 사용하는 것이 좋습니다. 항상 비동기가 가장 좋습니다. 완전히 동기화하는 것이 두 번째로 좋습니다. 비동기 동기화는 권장되지 않습니다.

그러나 비동기 동기화가 필요한 몇 가지 상황이 있습니다. 특히, 당신은 당신이 너무 호출 코드에 의해 제약을 동기화 될 (그리고 절대적으로 방법이 없습니다 다시 생각 또는 재 구조 비동기를 허용하는 코드), 그리고 당신이 코드를 비동기 호출. 이것은 매우 드문 상황이지만 때때로 발생합니다.

이 경우 브라운 필드 async개발 에 대한 내 기사에 설명 된 해킹 중 하나를 사용해야 합니다.

  • 차단 (예 :) GetAwaiter().GetResult(). 참고 이 교착 상태가 발생할 수 있습니다 (I 내 블로그에 설명 참조).
  • 스레드 풀 스레드에서 코드 실행 (예 :) Task.Run(..).GetAwaiter().GetResult(). 이것은 비동기 코드가 스레드 풀 스레드에서 실행될 수있는 경우에만 작동합니다 (즉, UI 또는 ASP.NET 컨텍스트에 종속되지 않음).
  • 중첩 된 메시지 루프. 이것은 비동기 코드가 특정 컨텍스트 유형이 아닌 단일 스레드 컨텍스트 만 가정하는 경우에만 작동합니다 (많은 UI 및 ASP.NET 코드에서 특정 컨텍스트를 예상 함).

중첩 된 메시지 루프는 재진입을 유발하기 때문에 모든 해킹 중에서 가장 위험 합니다. 재진입은 추론하기가 매우 까다 롭고 (IMO)는 Windows에서 대부분의 응용 프로그램 버그의 원인입니다. 특히 UI 스레드에 있고 작업 대기열에서 차단하는 경우 (비동기 작업이 완료 될 때까지 대기) CLR은 실제로 일부 메시지 펌핑을 수행합니다. 실제로 내부에서 일부 Win32 메시지 처리 합니다. 코드 . 오, 그리고 당신은 Chris Brumme 가 "무엇을 펌핑 할 것인지 정확히 아는 것이 좋지 않습니까? 불행히도 펌핑은 인간의 이해를 넘어서는 검은 예술입니다." , 그러면 우리는 정말로 알 희망이 없습니다.

따라서 UI 스레드에서 이와 같이 차단하면 문제가 발생합니다. 같은 기사의 또 다른 cbrumme 인용문 : "때때로 회사 내부 또는 외부의 고객은 STA [UI 스레드]에서 관리되는 차단 중에 메시지를 펌핑하고 있음을 발견합니다. 이는 매우 어렵다는 것을 알고 있기 때문에 합법적 인 문제입니다. 재진입에도 불구하고 강력한 코드를 작성합니다. "

네, 그렇습니다. 재진입에도 불구하고 강력한 코드를 작성하기가 매우 어렵습니다. 그리고 중첩 된 메시지 루프 는 재진입에도 불구하고 강력한 코드를 작성 하도록 합니다. 이유는 이 질문에 대한 허용 (그리고 가장 upvoted) 답변 입니다 매우 위험 연습한다.

다른 모든 옵션을 완전히 벗어난 경우-코드를 다시 디자인 할 수없고 비동기로 재구성 할 수 없습니다. 변경 불가능한 호출 코드가 동기화되도록 강제해야합니다. 다운 스트림 코드를 동기화 할 수 없습니다. -차단할 수 없습니다.-별도의 스레드에서 비동기 코드를 실행할 수 없습니다. 그런 다음 재진입을 고려해야합니다.

이 코너에서 자신을 찾으면 Dispatcher.PushFrameWPF 앱 과 같은 것을 사용하고 Application.DoEventsWinForm 앱에는 루프 를 사용하고 일반적인 경우에는 AsyncContext.Run.


이것은 나를 위해 잘 작동합니다.

public static class TaskHelper
{
    public static void RunTaskSynchronously(this Task t)
    {
        var task = Task.Run(async () => await t);
        task.Wait();
    }

    public static T RunTaskSynchronously<T>(this Task<T> t)
    {
        T res = default(T);
        var task = Task.Run(async () => res = await t);
        task.Wait();
        return res;
    }
}

내가 귀하의 질문을 올바르게 읽고 있다면 비동기 메서드에 대한 동기 호출을 원하는 코드가 일시 중단 된 디스패처 스레드에서 실행되고 있습니다. 그리고 비동기 메서드가 완료 될 때까지 해당 스레드 를 실제로 동 기적으로 차단 하려고합니다 .

C # 5의 비동기 메서드는 메서드를 내부에서 효과적으로 조각으로 자르고 Task전체 shabang의 전체 완료를 추적 할 수 있는 a 반환함으로써 구동됩니다. 그러나 잘게 잘린 메서드가 실행되는 방법은 await연산자에 전달되는 표현식의 유형에 따라 달라질 수 있습니다 .

대부분의 경우 await유형의 표현식에서을 사용하게 됩니다 Task. 태스크의 await패턴 구현 은를 연기한다는 점에서 "스마트"하며 SynchronizationContext기본적으로 다음이 발생합니다.

  1. 에 들어가는 스레드 await가 Dispatcher 또는 WinForms 메시지 루프 스레드에있는 경우 비동기 메서드의 청크가 메시지 큐 처리의 일부로 발생하는지 확인합니다.
  2. 에 들어가는 스레드 await가 스레드 풀 스레드에있는 경우 비동기 메서드의 나머지 청크는 스레드 풀의 어느 곳에서나 발생합니다.

그렇기 때문에 비동기 메서드 구현이 일시 중단되었지만 Dispatcher에서 나머지를 실행하려고 할 때 문제가 발생할 수 있습니다.

.... 백업! ....

질문해야합니다 . 비동기 메서드에서 동 기적으로 차단하려는 이유 는 무엇입니까? 그렇게하면 메서드가 비동기 적으로 호출되기를 원하는 이유에 대한 목적이 무효화됩니다. 일반적으로 awaitDispatcher 또는 UI 메서드에서 사용 시작하면 전체 UI 흐름을 비동기로 전환하는 것이 좋습니다. 예를 들어, 콜 스택이 다음과 같은 경우 :

  1. [상단] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing()- WPF또는 WinForms코드
  6. [메시지 루프] - WPF또는 WinForms메시지 루프

그런 다음 코드가 비동기를 사용하도록 변환되면 일반적으로

  1. [상단] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing()- WPF또는 WinForms코드
  6. [메시지 루프] - WPF또는 WinForms메시지 루프

실제로 응답

위의 AsyncHelpers 클래스는 중첩 된 메시지 루프처럼 동작하기 때문에 실제로 작동하지만 Dispatcher 자체에서 실행을 시도하는 대신 자체 병렬 메커니즘을 Dispatcher에 설치합니다. 이것이 문제에 대한 하나의 해결 방법입니다.

또 다른 해결 방법은 스레드 풀 스레드에서 비동기 메서드를 실행 한 다음 완료 될 때까지 기다리는 것입니다. 그렇게하는 것은 쉽습니다. 다음 스 니펫으로 수행 할 수 있습니다.

var customerList = TaskEx.RunEx(GetCustomers).Result;

최종 API는 Task.Run (...)이지만 CTP를 사용하려면 Ex 접미사가 필요합니다 ( 여기에 설명 ).


UI 스레드를 차단하지 않고 동기식으로 작업을 실행하는 가장 간단한 방법은 다음과 같이 RunSynchronously ()를 사용하는 것입니다.

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

제 경우에는 어떤 일이 발생할 때 발생하는 이벤트가 있습니다. 나는 그것이 몇 번 일어날 지 모른다. 따라서 이벤트에서 위의 코드를 사용하므로 실행될 때마다 작업이 생성됩니다. 작업은 동기식으로 실행되며 저에게 효과적입니다. 나는 이것이 얼마나 간단한 지 고려할 때 이것을 알아내는 데 너무 오래 걸린다는 것에 놀랐습니다. 일반적으로 권장 사항은 훨씬 더 복잡하고 오류가 발생하기 쉽습니다. 이것은 간단하고 깨끗했습니다.


나는 주로 단위 테스트 또는 Windows 서비스 개발에서 몇 번 직면했습니다. 현재 저는 항상이 기능을 사용합니다.

        var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
        {
            Trace.WriteLine("Task runSync Start");
            await TaskEx.Delay(2000); // Simulates a method that returns a task and
                                      // inside it is possible that there
                                      // async keywords or anothers tasks
            Trace.WriteLine("Task runSync Completed");
        })).Unwrap();
        Trace.WriteLine("Before runSync Wait");
        runSync.Wait();
        Trace.WriteLine("After runSync Waited");

간단하고 쉬우 며 문제가 없었습니다.


Microsoft.AspNet.Identity.Core 구성 요소에서이 코드를 찾았으며 작동합니다.

private static readonly TaskFactory _myTaskFactory = new 
     TaskFactory(CancellationToken.None, TaskCreationOptions.None, 
     TaskContinuationOptions.None, TaskScheduler.Default);

// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
    CultureInfo cultureUi = CultureInfo.CurrentUICulture;
    CultureInfo culture = CultureInfo.CurrentCulture;
    return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = cultureUi;
        return func();
    }).Unwrap<TResult>().GetAwaiter().GetResult();
}

참고로이 방법은 다음과 같습니다.

Task<Customer> task = GetCustomers();
task.Wait()

WinRT에서 작동합니다.

설명하겠습니다.

private void TestMethod()
{
    Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
    task.Wait(); // wait executing the method
    var customer = task.Result; // get's result.
    Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
    public Customer()
    {
        new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
    }
    public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
    return Task.Run(() => new Customer
    {
        Name = "MyName"
    });
}

또한이 접근 방식은 Windows Store 솔루션에만 적용됩니다!

참고 : 이 방법은 다른 비동기 메서드 내부에서 메서드를 호출하는 경우 스레드로부터 안전하지 않습니다 (@Servy의 주석에 따라).


코드에서 작업이 실행될 때까지 첫 번째 대기 하지만 시작하지 않았으므로 무기한 대기합니다. 이 시도:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

편집하다:

당신은 예외가 있다고 말합니다. 스택 추적을 포함한 자세한 내용을 게시하십시오.
Mono 에는 다음 테스트 케이스 가 포함되어 있습니다 .

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

이것이 당신을 위해 작동하는지 확인하십시오. 그렇지 않은 경우 가능성은 거의 없지만 Async CTP의 이상한 빌드가있을 수 있습니다. 작동하는 경우 컴파일러가 정확히 무엇을 생성하고 Task인스턴스화가이 샘플 과 어떻게 다른지 조사 할 수 있습니다 .

편집 # 2 :

나는 때 설명한 예외가 발생 리플렉터와 점검 m_action이다 null. 이것은 다소 이상하지만 Async CTP에 대한 전문가는 아닙니다. 내가 말했듯이, 당신은 당신의 코드를 컴파일하고 방법을 정확하게 볼 수 Task는 오는 방법 어떤 인스턴스화되고 m_actionIS를 null.


추신 : 비정기적인 반대표와의 거래는 무엇입니까? 자세히 설명 하시겠습니까?


다음과 같은 호출을 생성하지 마십시오.

Service.GetCustomers();

그것은 비동기가 아닙니다.


아래 코드 자르기 사용

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

이 답변은 .NET 4.5 용 WPF를 사용하는 모든 사용자를 위해 설계되었습니다.

Task.Run()GUI 스레드에서 실행을 시도하면 함수 정의에 키워드 task.Wait()가 없으면 무기한 중단됩니다 async.

이 확장 방법은 우리가 GUI 스레드에 있는지 확인하고, 그렇다면 WPF 디스패처 스레드에서 작업을 실행하여 문제를 해결합니다.

이 클래스는 MVVM 속성 또는 async / await를 사용하지 않는 다른 API에 대한 종속성과 같이 피할 수없는 상황에서 async / await 세계와 non-async / await 세계 사이의 접착제 역할을 할 수 있습니다.

/// <summary>
///     Intent: runs an async/await task synchronously. Designed for use with WPF.
///     Normally, under WPF, if task.Wait() is executed on the GUI thread without async
///     in the function signature, it will hang with a threading deadlock, this class 
///     solves that problem.
/// </summary>
public static class TaskHelper
{
    public static void MyRunTaskSynchronously(this Task task)
    {
        if (MyIfWpfDispatcherThread)
        {
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E99213. Task did not run to completion.");
            }
        }
        else
        {
            task.Wait();
            if (task.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E33213. Task did not run to completion.");
            }
        }
    }

    public static T MyRunTaskSynchronously<T>(this Task<T> task)
    {       
        if (MyIfWpfDispatcherThread)
        {
            T res = default(T);
            var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
            result.Wait();
            if (result.Status != DispatcherOperationStatus.Completed)
            {
                throw new Exception("Error E89213. Task did not run to completion.");
            }
            return res;
        }
        else
        {
            T res = default(T);
            var result = Task.Run(async () => res = await task);
            result.Wait();
            if (result.Status != TaskStatus.RanToCompletion)
            {
                throw new Exception("Error E12823. Task did not run to completion.");
            }
            return res;
        }
    }

    /// <summary>
    ///     If the task is running on the WPF dispatcher thread.
    /// </summary>
    public static bool MyIfWpfDispatcherThread
    {
        get
        {
            return Application.Current.Dispatcher.CheckAccess();
        }
    }
}

단순히 .Result;또는 .Wait()많은 사람들이 의견에서 말한 것처럼 교착 상태에 대한 위험이 있습니다. 우리 대부분은 oneliners를 좋아하기 때문에 이것을 사용할 수 있습니다..Net 4.5<

비동기 메서드를 통해 값 획득 :

var result = Task.Run(() => asyncGetValue()).Result;

비동기 메서드를 동시에 호출

Task.Run(() => asyncMethod()).Wait();

의 사용으로 인해 교착 상태 문제가 발생하지 않습니다 Task.Run.

출처:

https://stackoverflow.com/a/32429753/3850405


.Net 4.6에서 테스트되었습니다. 또한 교착 상태를 피할 수 있습니다.

비동기 메서드의 경우 Task.

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

비동기 메서드 반환의 경우 Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

편집 :

호출자가 스레딩 풀 스레드에서 실행 중이거나 호출자가 작업중인 경우에도 일부 상황에서 여전히 데드락이 발생할 수 있습니다.


다음 도우미 방법으로도 문제를 해결할 수 있다고 생각합니다.

private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
    {
        TResult result = default(TResult);
        var autoResetEvent = new AutoResetEvent(false);

        Task.Run(async () =>
        {
            try
            {
                result = await func();
            }
            catch (Exception exc)
            {
                mErrorLogger.LogError(exc.ToString());
            }
            finally
            {
                autoResetEvent.Set();
            }
        });
        autoResetEvent.WaitOne();

        return result;
    }

다음과 같은 방법으로 사용할 수 있습니다.

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);

이것은 나를 위해 작동합니다

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public static class AsyncHelper
    {
        private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

        public static void RunSync(Func<Task> func)
        {
            _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }

        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
        }
    }

    class SomeClass
    {
        public async Task<object> LoginAsync(object loginInfo)
        {
            return await Task.FromResult(0);
        }
        public object Login(object loginInfo)
        {
            return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
            //return this.LoginAsync(loginInfo).Result.Content;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var someClass = new SomeClass();

            Console.WriteLine(someClass.Login(1));
            Console.ReadLine();
        }
    }
}

나는 SpinWait이 이것에 대해 꽤 잘 작동한다는 것을 발견했습니다.

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

위의 접근 방식은 .Result 또는 .Wait ()를 사용할 필요가 없습니다. 또한 작업이 완료되지 않은 경우 영원히 멈춰 있지 않도록 제한 시간을 지정할 수 있습니다.


wp8에서 :

싸다:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

불러라:

GetCustomersSynchronously();

    private int GetSync()
    {
        try
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            int result = null;

            Parallel.Invoke(async () =>
            {
                result = await SomeCalcAsync(5+5);
                mre.Set();
            });

            mre.WaitOne();
            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }

또는 다음과 같이 갈 수 있습니다.

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

이를 컴파일하려면 확장 어셈블리를 참조해야합니다.

System.Net.Http.Formatting

나를 위해 작동하는 다음 코드를 시도하십시오.

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}

참고 URL : https://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously

반응형