비동기 Task <T> 메서드를 어떻게 동 기적으로 실행합니까?


628

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()

46
"비동기 메서드를 동 기적으로 호출하는 방법"이라는 질문에 대한 가장 좋은 대답은 "하지 마십시오"입니다. 있습니다 해킹 작업에 강제로 시도,하지만 그들은 모두 매우 미묘한 함정이있다. 대신,이를 수행하기 위해 "필요한"코드를 백업하고 수정하십시오.
Stephen Cleary

57
@Stephen Cleary 동의하지만 때로는 코드가 비동기 / 대기를 사용하지 않는 타사 API에 의존하는 경우와 같이 때로는 피할 수없는 경우가 있습니다. 또한 MVVM을 사용할 때 WPF 속성에 바인딩하는 경우 속성에서 지원되지 않으므로 async / await를 사용할 수 없습니다.
Contango

3
@StephenCleary 항상 그런 것은 아닙니다. GeneXus 에서 가져올 DLL을 작성 중입니다 . async / await 키워드를 지원하지 않으므로 동기 메소드 만 사용해야합니다.
Dinei

5
@StephenCleary 1) GeneXus는 세 번째 pt 도구이며 소스 코드에 액세스 할 수 없습니다. 2) GeneXus는 심지어 "함수"의 구현을 가지고 있지 않기 때문에 이런 유형의 "콜백"을 어떻게 구현할 수 있는지 알 수 없습니다. Task동 기적으로 사용하는 것보다 해결 방법이 더 어려울 것입니다 . 3) GeneXus를 MongoDB C # 드라이버 와 통합 하고 있는데, 일부 메소드는 비동기식으로 만 노출됩니다
Dinei

1
@ygoe :와 같은 비동기 호환 잠금을 사용하십시오 SemaphoreSlim.
Stephen Cleary

답변:


456

다음은 모든 경우 (정지 된 발송자를 포함)에서 작동하는 해결 방법입니다. 내 코드가 아니며 여전히 완전히 이해하기 위해 노력하고 있지만 작동합니다.

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

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

28
이것이 어떻게 작동하는지에 대한 배경 지식을 얻기 위해 Stephen Toub (Mr Parallel)는 이에 관한 일련의 게시물을 썼습니다. 1 부 2 부 3 부
Cameron MacFarland

18
람다에서 작업을 래핑하지 않고 작동하도록 John의 코드를 업데이트했습니다 : github.com/tejacques/AsyncBridge . 기본적으로 using 문을 사용하여 비동기 블록으로 작업합니다. using 블록 내부의 모든 것은 끝에서 대기하면서 비동기 적으로 발생합니다. 단점은 콜백에서 직접 작업을 풀어야한다는 것입니다.하지만 특히 여러 비동기 함수를 한 번에 호출 해야하는 경우 여전히 우아합니다.
Tom Jacques

17
@StephenCleary 나는 일반적으로 때로는 하나가 불가능한 상황에서 자신을 발견, 코드가 모든 방법을 아래로 비동기해야한다는 당신과 동의하지만 동기 호출로 강제로. 기본적으로 내 상황은 모든 데이터 액세스 코드가 비동기 방식이라는 것입니다. 사이트 맵을 기반으로 사이트 맵을 작성해야했고 사용중인 타사 라이브러리는 MvcSitemap이었습니다. 이제 DynamicNodeProviderBase기본 클래스 를 통해 확장 하면 async메서드 로 선언 할 수 없습니다 . 새 라이브러리로 바꾸거나 동기 op를 호출해야했습니다.
justin.lovell

6
@ justin.lovell : 예, 라이브러리 제한으로 인해 적어도 라이브러리가 업데이트 될 때까지 해킹을 당할 수 있습니다. MvcSitemap은 해킹이 필요한 상황 중 하나 인 것 같습니다 (MVC 필터 및 자식 작업도 마찬가지 임). 나는 이런 사람들이 필요 하지 않을 때 너무 자주 사용되기 때문에 사람들을 일반적으로 설득 하지 않습니다. 특히 MVC의 경우 일부 ASP.NET/MVC API는을 가지고 있다고 가정 AspNetSynchronizationContext하므로 해당 API를 호출하면이 특정 핵이 작동하지 않습니다.
Stephen Cleary

5
이 코드는 작동하지 않습니다. 풀 스레드에서 호출되면 스레드 기아 교착 상태가 발생할 수 있습니다. 호출자는 작업이 완료되기를 기다리는 것을 차단하며 스레드 풀을 모두 사용한 경우에는 발생하지 않을 수 있습니다. 이 기사를 참조 하십시오 .
ZunTzu

318

알린다 이 대답은 세 가지 살입니다. 나는 주로 .Net 4.0에 대한 경험을 바탕으로 작성했으며 4.5에 대해서는 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();

67
.Result특정 시나리오에서 교착 상태를 일으킬 수 있습니다
Jordy Langen

122
Result내 블로그에 설명 된 것처럼 코드 에서 교착 상태를 쉽게 일으킬async있습니다 .
Stephen Cleary

8
@StephenCleary 귀하의 게시물을 읽고 직접 시도했습니다. 나는 솔직히 Microsoft의 누군가가 취했다고 생각합니다 ... winforms 및 배경 스레드와 같은 문제입니다 ....
AK_

9
이 문제는 비동기 메서드로 반환되는 작업과 관련이 있습니다. 이러한 종류의 작업은 이미 시작, 실행 또는 취소 되었을 수 있으므로 Task.RunSynchronously 메서드를 사용 하면 InvalidOperationException 이 발생할 수 있습니다 . MSDN 페이지 : Task.RunSynchronously Method를 참조하십시오 . 또한 해당 작업은 Task.Factory.StartNew 또는 Task.Run 메서드 (비동기 메서드 내)에 의해 만들어 질 수 있으므로 다시 시작하는 것은 위험합니다. 런타임시 일부 경쟁 조건이 발생할 수 있습니다. 반면에 Task.WaitTask.Result 는 교착 상태가 발생할 수 있습니다.
sgnsajgon

4
Run Synchronously 나를 위해 일했습니다 ... 뭔가 빠졌는지 모르겠지만 이것이 표시된 답변의 공포보다 선호되는 것 같습니다. 나는 단지 멈추기 위해 코드를 테스트하기 위해 비동기를 끄는 방법을 찾고있었습니다. ui에서 교수형
JonnyRaa

121

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

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

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

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

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

또한 GetAwaiter오리 형식 이기 때문에 작업뿐만 아니라 비동기 메서드 ( 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()

1
간단한 것들을 위해 나를 위해 일합니다. 또한 메서드가 IAsyncOperation을 반환하면 먼저 작업으로 변환해야합니다. BlahAsync (). AsTask (). GetAwaiter (). GetResult ();
Lee McPherson

3
이로 인해 asmx 웹 메서드 내부에서 교착 상태가 발생했습니다. 그럼에도 불구하고, Task.Run ()에있어서 전화를 포장했다 그것은 작동 :.. Task.Run (() => BlahAsync ()) GetAwaiter () getResult를 ()
아우 바레토

나는 람다를 포함하지 않기 때문에이 방법을 문법적으로 가장 좋아합니다.
dythim

25
다른 사람의 답변을 편집하여 자신의 링크를 삽입하지 마십시오. 당신의 대답이 더 낫다고 생각한다면, 대신 의견으로 남겨 두십시오.
Rachel

1
docs.microsoft.com/en-us/dotnet/api/…에 대해 말합니다 GetAwaiter(). "이 방법은 코드에서 직접 사용하기보다는 컴파일러 사용자를위한 것입니다."
Theophilus

75

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

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; 

3
그런 다음 task.Wait ()를 호출하십시오. 데이터 유형은 단순히 작업입니다.
Michael L Perry

1
DoSomethingAsync () 가 전체적으로 장기 실행 비동기 메소드 라고 가정하지만 (내부적으로 장기 실행 태스크를 기다리고 있음) 호출자에게 플로우 제어를 신속하게 되돌려 서 람다 인수 작업도 빠르게 종료됩니다. Tusk.Run ()의 결과는 Task <Task> 또는 Task <Task <>> 일 수 있으므로 외부 작업의 결과가 빨리 완료되지만 내부 작업 (비동기 메서드에서 장시간 실행되는 작업 대기 중) 여전히 실행 중입니다. 결론은 @ J.Lennon post에서와 같이 Unwrap () 접근 방식 을 사용 하여 비동기 메소드의 동기 동작을 달성 해야한다는 것입니다 .
sgnsajgon

5
@sgnsajgon 당신이 틀 렸습니다. Task.Run은 이미 결과를 자동으로 래핑 해제한다는 점에서 Task.Factory.StartNew와 다릅니다. 이 기사를 참조 하십시오 .
ZunTzu

1
Task.Run(DoSomethingAsync)대신 쓸 수 있습니까 ? 이렇게하면 한 수준의 델리게이트가 제거됩니다.
ygoe

1
네. 그러나 Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());보다 명시 적으로 반대 방향으로 가면 Tasks <Task <MyResult >>를 반환 할 수있는 @sgnsajgon의 우려를 해결합니다. Task.Run의 올바른 오버로드는 어느 쪽이든 선택되지만 비동기 대리자는 의도를 분명히합니다.
마이클 L 페리

57

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

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

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

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

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

비동기 동기화를 수행하는 것보다 거의 항상 더 나은 답변이 있습니다.

귀하의 상황에서 가능하지 않은 경우 (여기서 상황을 설명 하는 질문을 통해 이것을 알고 있음 ) 동기 코드를 사용하는 것이 좋습니다. 모든면에서 비동기가 가장 좋습니다. 끝까지 동기화하는 것이 가장 좋습니다. 비동기 동기화는 권장되지 않습니다.

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

이 경우 브라운 필드 async개발 에 대한 기사에서 설명한 해킹 중 하나 , 특히 다음 을 사용해야 합니다.

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

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

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

그렇습니다. 재진입에 강인한 코드를 작성하는 것은 매우 어렵다. 또한 중첩 된 메시지 루프를 사용 하면 재진입시 강력한 코드를 작성할 수 있습니다. 이유는 이 질문에 대한 허용 (그리고 가장 upvoted) 답변 입니다 매우 위험 연습한다.

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

이 코너에서 자신을 찾으면 Dispatcher.PushFrameWPF 앱 과 같은 것을 사용하고 , Application.DoEventsWinForm 앱 과 함께 반복하고 , 일반적인 경우에는 내 자신의 것을 사용하는 것이 좋습니다 AsyncContext.Run.


스티븐 또 다른 매우 유사있다 qestion 당신이 너무 멋진 대답을 제공했다. 당신은 그들 중 하나가 중복으로 닫히거나 병합 요청으로 시작되거나 메타를 먼저 제기 할 수 있다고 생각합니까 (각 질문은 ~ 200K 조회수가 200 이상입니다)? 제안?
Alexei Levenkov

1
@ AlexeiLevenkov : 몇 가지 이유로 제대로하고 싶지 않습니다 : 1) 연결된 질문에 대한 답변이 상당히 오래되었습니다. 2) 기존 SO Q / A보다 완벽하다고 생각 되는 주제에 대해 전체 기사를 작성했습니다 . 3)이 질문에 대한 대답은 매우 인기가 있습니다. 4) 나는 그 대답에 반대하는 것을 강력히 반대한다. 그래서, 이것을 두배로 폐쇄하는 것은 권력 남용 일 것입니다. 이 (또는 합병)의 멍청이로 위험한 답변에 더 힘을 실어 줄 것입니다. 나는 그것을 내버려두고 지역 사회에 맡깁니다.
Stephen Cleary

확인. 어떤 식 으로든 메타를 메타로 가져 오는 것을 고려할 것입니다.
Alexei Levenkov

9
이 대답은 내 머리 위로 먼 길을 간다. "Async를 완전히 사용하십시오" 는 명확하게 따라갈 수 없기 때문에 혼란스러운 조언입니다. 비동기 Main()메서드가 있는 프로그램은 컴파일되지 않습니다. 어떤 시점에서 당신은 한 가지고 동기 및 비동기 세계 사이의 격차를 해소 할 수 있습니다. " 매우 드문 상황" 이 아니며 문자 그대로 비동기 메소드를 호출하는 모든 프로그램에서 필요합니다. "sync-over-async" 옵션은 없습니다 . 단지 현재 작성중인 방법으로 처리하는 대신 호출 방법까지 부담을 줄이는 옵션입니다.
Mark Amery

1
큰. 나는 넣을에 대해 해요 async지금 내 응용 프로그램에서 모든 메소드에. 그리고 그것은 많이입니다. 이것이 기본값이 될 수 없습니까?
ygoe

25

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

C # 5의 비동기 메소드는 효과적으로 후드 아래에서 메소드를 잘게 자르고 Task전체 shabang의 전체 완료를 추적 할 수있는를 리턴하여 구동됩니다. 그러나 잘린 메소드가 실행되는 방식은 await연산자에 전달 된 표현식의 유형에 따라 다릅니다 .

대부분의 경우 await유형 표현식을 사용하게 됩니다 Task. await패턴 의 Task 구현 은 "smart" SynchronizationContext이며 이는 기본적으로 다음을 발생시킵니다.

  1. 를 입력하는 스레드 await가 디스패처 또는 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 접미사가 필요합니다 ( 여기 설명 ).


자세한 설명은 +1이지만 TaskEx.RunEx(GetCustomers).Result일시 중단 된 발송자 스레드에서 실행될 때 응용 프로그램이 중단됩니다. 또한 GetCustomers () 메서드는 일반적으로 비동기로 실행되지만 한 상황에서는 동기식으로 실행해야하므로 메서드의 동기화 버전을 작성하지 않고도이를 수행 할 수있는 방법을 찾고있었습니다.
Rachel

"비동기 방식을 동기식으로 차단하려는 이유는 무엇입니까?"에 +1 항상 방법을 올바르게 사용하는 async방법이 있습니다. 중첩 루프는 피해야합니다.
Stephen Cleary

24

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

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

Task.Wait 문으로 인해 내부 대기 상태가 아닌 외부 태스크 ( Task.Run에 의해 생성됨 ) 가 대기하기 때문에 Task.Unwrap 메소드 도 사용해야 합니다. t 확장 메소드의 매개 변수로 전달 된 태스크. 귀하의 Task.Run의 방법을 반환하지 작업 <T>,하지만 작업 <작업 <T >>. 일부 간단한 시나리오에서 TaskScheduler 최적화로 인해 솔루션이 작동 할 수 있습니다 (예 : TryExecuteTaskInline 메서드를 사용하여 대기 작업 중 현재 스레드 내에서 작업을 실행 하는 경우) . 답변에 대한 내 의견을 살펴보십시오 .
sgnsajgon

1
맞지 않습니다. Task.Run은 Task <T>를 반환합니다. 이 과부하 참조 msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspx
Clement

이것은 어떻게 사용됩니까? WPF에서이 교착 상태는 다음과 같습니다.MyAsyncMethod().RunTaskSynchronously();
ygoe

18

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

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

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


1
그러나 비동기 코드가 필요한 것을 반환 할 때이 방법을 어떻게 사용할 수 있습니까?
S.Serpooshan

16

나는 주로 단위 테스트 또는 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");

간단하고 쉽고 문제가 없었습니다.


이것은 나를 위해 교착 상태가 아닌 유일한 것입니다.
AndreFeijo

15

이 코드는 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();
}


13

약간의 메모-이 접근법 :

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 스토어 솔루션에만 적용됩니다!

참고 : 이 방법은 다른 비동기 메소드 내에서 메소드를 호출하면 스레드 안전하지 않습니다 (@Servy의 의견에 따라)


이 솔루션을 설명했습니다. 편집 섹션을 확인하십시오.
RredCat

2
비동기 상황에서 호출 될 때 교착 상태가 발생하기 쉽습니다.
Servy

@Servy는 의미가 있습니다. 그래서 Wait (timeOut)을 사용하여 올바르게 얻을 수 있습니다.
RredCat

1
그런 다음 작업이 실제로 수행되지 않을 때 시간 초과에 도달하는 데 대해 걱정할 필요가 있습니다. 이는 매우 나쁘며 교착 상태가있는 경우 시간 초과까지 대기하는 시간 (그리고 여전히 계속 진행 중) 완료되지 않은 경우). 따라서 문제가 해결되지 않습니다.
Servy

@Servy CancellationToken솔루션 을 구현 해야하는 것 같습니다 .
RredCat

10

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

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

편집하다:

당신은 예외가 있다고 말합니다. 스택 추적을 포함하여 자세한 내용을 게시하십시오.
모노 에는 다음과 같은 테스트 사례가 포함됩니다.

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

        Assert.AreEqual (1, val);
}

이것이 당신을 위해 작동하는지 확인하십시오. 그렇지 않은 경우에는 매우 드물지만 비동기 CTP가 이상하게 빌드 될 수 있습니다. 작동하는 경우 컴파일러에서 정확히 생성하는 내용과 Task인스턴스화가이 샘플 과 어떻게 다른지 조사 할 수 있습니다 .

편집 # 2 :

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


PS 가끔 다운 보트를 다루는 것은 무엇입니까? 정교하게 관리?


시도한 코드를 좀 더 명확하게하기 위해 질문을 조정했습니다. RunSynchronously는 오류를 반환합니다 RunSynchronously may not be called on a task unbound to a delegate. 그 모든 결과는 중국어에 있기 때문에 구글은 ... 아무 도움이되지 않습니다
레이첼

차이점은 작업을 생성하지 않고 실행하려고한다는 것입니다. 대신 await키워드를 사용할 때 비동기 메서드로 작업을 만듭니다. 이전 의견에 게시 된 예외는 내가 얻을 수있는 예외이지만 Google에서 찾을 수 없으며 원인이나 해결책을 찾을 수없는 몇 가지 중 하나입니다.
Rachel

1
async그리고 async키워드는 더 구문 설탕에 비해 아무것도 아니다. 컴파일러가 생성하는 코드 생성 Task<Customer>GetCustomers()내가 먼저 볼 것 곳은 그래서를. 예외에 대해서는 예외 유형과 스택 추적이 없으면 쓸 수없는 예외 메시지 만 게시했습니다. 예외의 ToString()메소드를 호출하고 질문에 결과를 게시하십시오.
Dan Abramov

@ gaearon : 원래 질문에 예외 세부 사항과 스택 추적을 게시했습니다.
Rachel

2
@gaearon 귀하의 게시물이 질문에 해당되지 않기 때문에 귀하에게 공감대가 있다고 생각합니다. 간단한 Task-returning 메서드가 아니라 async-await 메서드에 대해 설명합니다. 또한 async-await 메커니즘은 구문 설탕이지만 그렇게 사소한 것은 아닙니다. 연속성, 컨텍스트 캡처, 로컬 컨텍스트 재개, 향상된 로컬 예외 처리 등이 있습니다. 그런 다음 비동기 메서드의 결과에 따라 비동기 메서드는 현재 적어도 일정이 설정되어 있고 두 번 이상 실행 상태에있는 작업을 반환해야하므로 비동기 메서드의 결과에 대해 RunSynchronously 메서드를 호출하면 안됩니다 .
sgnsajgon

9

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

async 메서드를 반환 Task합니다.

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

비동기 메소드 리턴의 경우 Task<T>

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

편집 :

호출자가 스레드 풀 스레드에서 실행 중이거나 호출자가 작업에있는 경우에도 일부 상황에서 교착 상태가 발생할 수 있습니다.


1
거의 8 년 후 나의 answar :) 두 번째 예-주로 사용되는 모든 예약 된 컨텍스트 (콘솔 앱 / .NET 코어 / 데스크톱 앱 / ...)에서 교착 상태가 발생합니다. 여기에 내가 지금 이야기하고있는 더 많은 개요가 있습니다 : medium.com/rubrikkgroup/…
W92

Result동기식 호출을 원하면 작업에 완벽하고 그렇지 않으면 완전히 위험합니다. 이름 Result이나 정보 Result에는 차단 호출임을 나타내는 것이 없습니다. 실제로 이름을 바꿔야합니다.
Zodman

5

아래 코드 스니핑에서 사용

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

4

다음과 같은 호출을 작성하십시오.

Service.GetCustomers();

그것은 비동기 적이 지 않습니다.


4
이것이 작동하지 않으면 내가 할 일이 될 것입니다 ... 비동기 버전 외에도 동기화 버전 만들기
Rachel

3

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

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

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

이 클래스는 MVVM 속성이나 async / await를 사용하지 않는 다른 API에 대한 종속성과 같이 피할 수없는 상황에서 async / await 세계와 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();
        }
    }
}

3

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

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

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

비동기 메서드를 동 기적으로 호출

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

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

출처:

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


1

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

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

1
투표에 대해 설명해주세요
donttellya

2
...이 답변이 왜 투표에 실패했는지 여전히 관심이 있습니까?
donttellya

"동 기적으로"사실이 아닙니다. 두 개의 스레드를 만들고 첫 번째 결과를 기다립니다.
tmt

그리고 모든 것을 제쳐두고 이것은 매우 나쁜 생각입니다.
Dan Pantry

1
방금 거의 동일한 코드를 작성했지만 자동 재설정 이벤트 대신 SemaphoreSlim을 사용했습니다. 나는 이것을 더 빨리 보길 바란다. 교착 상태를 방지하고 실제 비동기 시나리오에서와 동일한 방식으로 비동기 코드를 실행하는이 방법을 찾습니다. 이것이 왜 나쁜 생각인지 확실하지 않습니다. 위에서 본 다른 접근법보다 훨씬 깨끗합니다.
tmrog 2016 년

0

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

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

-1

나는 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 ()를 사용할 필요가 없습니다. 또한 작업이 완료되지 않은 경우를 대비하여 시간이 초과되지 않도록 시간 초과를 지정할 수 있습니다.


1
공감대는 누군가이 방법을 좋아하지 않는다고 제안합니다. 이것의 단점에 대해 언급 할 수있는 사람이 있습니까?
Grax32

downvoter가 왜 downvote를 받았는지 말하지 않는다면 누군가 그것을지지 할 수 있습니까? :-)
Curtis

1
이것은 폴링 (회전)이며, 델리게이트는 초당 최대 1000 번 풀에서 스레드를 가져옵니다. 작업 완료 후 즉시 제어를 반환하지 않을 수 있습니다 (최대 10 + ms 오류). 시간 초과로 완료되면 작업이 계속 실행되어 시간 초과가 실제로는 쓸모 없게됩니다.
Sinatr

실제로, 나는 이것을 내 코드의 모든 곳에서 사용하고 있으며 조건이 충족되면 SpinWaitSpinUntil ()이 즉시 종료됩니다. 따라서 '조건 충족'또는 시간 초과 중 먼저 오는 것이 있으면 작업이 종료됩니다. 계속 실행되지 않습니다.
Curtis

-3

wp8에서 :

싸다:

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

불러라:

GetCustomersSynchronously();

3
작업이 생성자로부터 델리게이트를 기다리지 않기 때문에 (이는 델리게이트가 아닌 델리게이트) 태스크가 작동하지 않습니다.
Rico Suter

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

-5

아니면 그냥 갈 수 있습니다 :

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

이것을 컴파일하려면 확장 어셈블리를 참조하십시오.

System.Net.Http.Formatting

-9

다음 코드를 사용해보십시오.

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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.