programing

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

closeapi 2023. 5. 31. 15:57
반응형

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

나는 있습니다.async방법:

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

이 메서드를 동기식 메서드에서 호출해야 합니다.

어떻게 하면 복제하지 않고 이 작업을 수행할 수 있습니까?GenerateCodeAsync이것이 동기적으로 작동하기 위한 방법?

갱신하다

그러나 합리적인 해결책을 찾을 수 없습니다.

하지만, 나는 그것을 봅니다.HttpClient이미 이 패턴을 구현했습니다.

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}

당신은 할 수 있습니다.Result결과를 사용할 수 있을 때까지 스레드를 차단하는 작업 속성:

string code = GenerateCodeAsync().Result;

참고: 경우에 따라 교착 상태가 발생할 수 있습니다.:Result메인 스레드를 차단하여 비동기 코드의 나머지가 실행되지 않도록 합니다.이러한 문제가 발생하지 않도록 다음 옵션을 사용할 수 있습니다.

  • 더하다.ConfigureAwait(false) 메소드로 또는

  • 스레드 풀 스레드에서 비동기 메서드를 명시적으로 실행하고 완료될 때까지 기다립니다.

      string code = Task.Run(() => GenerateCodeAsync).Result;
    

이것은 당신이 단지 무심코 추가해야 한다는 것을 의미하지 않습니다..ConfigureAwait(false)당신의 모든 비동기 통화 후에!에 대한 을 참조하십시오..ConfigureAwait(false)다음 블로그 게시물을 참조하십시오.

.GetAwaiter())를작업이 완료될 ()를 GetResult()).

string code = GenerateCodeAsync().GetAwaiter().GetResult();

딜러, 람다 식을 사용하여 이 작업을 수행할 수 있어야 합니다.

private void button2_Click(object sender, EventArgs e)
    {

        label1.Text = "waiting....";

        Task<string> sCode = Task.Run(async () =>
        {
            string msg =await GenerateCodeAsync();
            return msg;
        });

        label1.Text += sCode.Result;

    }

    private Task<string> GenerateCodeAsync()
    {
        return Task.Run<string>(() => GenerateCode());
    }

    private string GenerateCode()
    {
        Thread.Sleep(2000);
        return "I m back" ;
    }

Microsoft Identity에는 비동기 메서드를 동기적으로 호출하는 확장 메서드가 있습니다.예를 들어 GenerateUserIdentityAsync() 메서드와 동일한 CreateIdentity() 메서드가 있습니다.

사용자 관리자를 확인하는경우내선 번호.CreateIdentity()는 다음과 같습니다.

 public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
        string authenticationType)
        where TKey : IEquatable<TKey>
        where TUser : class, IUser<TKey>
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
    }

이제 SyncHelper가 무엇인지 살펴보겠습니다.RunSync는

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

이것은 비동기 방식을 위한 포장지입니다.그리고 결과의 데이터를 읽지 마십시오. 결과가 ASP의 코드를 차단할 수 있습니다.

다른 방법이 있습니다 - 저에게는 의심스럽지만, 당신도 그것을 고려할 수 있습니다.

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();

저는 이 메서드를 동기식 메서드에서 호출해야 합니다.

GenerateCodeAsync().Result또는GenerateCodeAsync().Wait()다른 대답이 시사하듯이이렇게 하면 현재 스레드가 다음 시간까지 차단됩니다.GenerateCodeAsync완료되었습니다.

그러나 질문에 asp. 태그가 지정되어 있으며, 다음과 같은 의견도 남겨두었습니다.

저는 asp.net 이 그렇게 많은 줄의 코드를 쓰는 것보다 훨씬 더 쉽게 처리할 수 있다고 생각하면서 더 간단한 해결책을 기대하고 있었습니다.

요점은 ASP.NET의 비동기 메서드에서 차단하면 안 된다는 것입니다.이것은 당신의 웹 앱의 확장성을 감소시킬 것이고, 교착 상태를 만들 것입니다.await내부의 계속.GenerateCodeAsync에 게시됨AspNetSynchronizationContext) 사용.Task.Run(...).Result무언가를 풀 스레드로 오프로드한 다음 차단하면 주어진 HTTP 요청을 처리하는 데 +1개의 스레드가 더 발생하기 때문에 확장성이 더욱 저하됩니다.

ASP.NET은 비동기 컨트롤러(ASP.NET MVC 및 웹 API)를 통해 또는 직접적으로 비동기 방식을 지원합니다.AsyncManager그리고.PageAsyncTask고전적인 ASP.NET에서.사용하셔야 합니다.자세한 내용은 이 답변을 참조하십시오.

교착 상태를 방지하기 위해 항상 사용합니다.Task.Run()@Heinzi가 언급하는 비동기 방식을 동시에 호출해야 할 때.

그러나 비동기 메서드가 매개 변수를 사용하는 경우 메서드를 수정해야 합니다.예를들면Task.Run(GenerateCodeAsync("test")).Result다음과 같은 오류가 표시됩니다.

인수 1: '에서 변환할 수 없습니다.System.Threading.Tasks.Task<string>'시스템'으로.'액션'

대신 이를 다음과 같이 부를 수 있습니다.

string code = Task.Run(() => GenerateCodeAsync("test")).Result;

이 스레드의 대부분의 답변은 복잡하거나 교착 상태가 됩니다.

다음 방법은 간단하고 교착 상태를 피할 수 있습니다. 왜냐하면 우리는 작업이 끝날 때까지 기다렸다가 결과를 얻을 수 있기 때문입니다.

var task = Task.Run(() => GenerateCodeAsync()); 
task.Wait();
string code = task.Result;

게다가, 여기 정확히 같은 것에 대해 이야기하는 MSDN 기사에 대한 참조가 있습니다 - https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/

저는 기본 비동기 작업의 예외를 처리하고 전파하는 이 접근 방식을 사용하고 있습니다.

    private string RunSync()
    {
        var task = Task.Run(async () => await GenerateCodeService.GenerateCodeAsync());
        if (task.IsFaulted && task.Exception != null)
        {
            throw task.Exception;
        }

        return task.Result;
    }

비동기식으로 비동기식 작업이 완료될 때까지 기다렸다가 완료를 표시하도록 수동 재설정 이벤트를 설정하는 일부 확장 메서드는 어떻습니까?

참고: 작업을 사용할 수 있습니다.실행(), 그러나 확장 메서드는 정말 원하는 것을 표현하는 더 깨끗한 인터페이스입니다.

확장 사용 방법을 보여주는 테스트:

    [TestClass]
    public class TaskExtensionsTests
    {
        [TestMethod]
        public void AsynchronousOperationWithNoResult()
        {
            SampleAsynchronousOperationWithNoResult().AwaitResult();
        }

        [TestMethod]
        public void AsynchronousOperationWithResult()
        {
            Assert.AreEqual(3, SampleAsynchronousOperationWithResult(3).AwaitResult());
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousOperationWithNoResultThrows()
        {
            SampleAsynchronousOperationWithNoResultThrows().AwaitResult();
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousOperationWithResultThrows()
        {
            SampleAsynchronousOperationWithResultThrows(3).AwaitResult();
        }

        private static async Task SampleAsynchronousOperationWithNoResult()
        {
            await Task.Yield();
        }

        private static async Task<T> SampleAsynchronousOperationWithResult<T>(T result)
        {
            await Task.Yield();
            return result;
        }

        private static async Task SampleAsynchronousOperationWithNoResultThrows()
        {
            await Task.Yield();
            throw new Exception();
        }

        private static async Task<T> SampleAsynchronousOperationWithResultThrows<T>(T result)
        {
            await Task.Yield();
            throw new Exception();
        }

        [TestMethod]
        public void AsynchronousValueOperationWithNoResult()
        {
            SampleAsynchronousValueOperationWithNoResult().AwaitResult();
        }

        [TestMethod]
        public void AsynchronousValueOperationWithResult()
        {
            Assert.AreEqual(3, SampleAsynchronousValueOperationWithResult(3).AwaitResult());
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousValueOperationWithNoResultThrows()
        {
            SampleAsynchronousValueOperationWithNoResultThrows().AwaitResult();
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousValueOperationWithResultThrows()
        {
            SampleAsynchronousValueOperationWithResultThrows(3).AwaitResult();
        }

        private static async ValueTask SampleAsynchronousValueOperationWithNoResult()
        {
            await Task.Yield();
        }

        private static async ValueTask<T> SampleAsynchronousValueOperationWithResult<T>(T result)
        {
            await Task.Yield();
            return result;
        }

        private static async ValueTask SampleAsynchronousValueOperationWithNoResultThrows()
        {
            await Task.Yield();
            throw new Exception();
        }

        private static async ValueTask<T> SampleAsynchronousValueOperationWithResultThrows<T>(T result)
        {
            await Task.Yield();
            throw new Exception();
        }
    }

내선 번호

    /// <summary>
    /// Defines extension methods for <see cref="Task"/> and <see cref="ValueTask"/>.
    /// </summary>
    public static class TaskExtensions
    {
        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking; ignoring cancellation.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitCompletion(this ValueTask task)
        {
            new SynchronousAwaiter(task, true).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking; ignoring cancellation.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitCompletion(this Task task)
        {
            new SynchronousAwaiter(task, true).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        /// <typeparam name="T">
        /// The result type of the operation.
        /// </typeparam>
        /// <returns>
        /// The result of the operation.
        /// </returns>
        public static T AwaitResult<T>(this Task<T> task)
        {
            return new SynchronousAwaiter<T>(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitResult(this Task task)
        {
            new SynchronousAwaiter(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the pending operation.
        /// </param>
        /// <typeparam name="T">
        /// The result type of the operation.
        /// </typeparam>
        /// <returns>
        /// The result of the operation.
        /// </returns>
        public static T AwaitResult<T>(this ValueTask<T> task)
        {
            return new SynchronousAwaiter<T>(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the pending operation.
        /// </param>
        public static void AwaitResult(this ValueTask task)
        {
            new SynchronousAwaiter(task).GetResult();
        }

        /// <summary>
        /// Ignore the <see cref="OperationCanceledException"/> if the operation is cancelled.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the asynchronous operation whose cancellation is to be ignored.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/> representing the asynchronous operation whose cancellation is ignored.
        /// </returns>
        public static async Task IgnoreCancellationResult(this Task task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
        }

        /// <summary>
        /// Ignore the <see cref="OperationCanceledException"/> if the operation is cancelled.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose cancellation is to be ignored.
        /// </param>
        /// <returns>
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose cancellation is ignored.
        /// </returns>
        public static async ValueTask IgnoreCancellationResult(this ValueTask task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
        }

        /// <summary>
        /// Ignore the results of an asynchronous operation allowing it to run and die silently in the background.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the asynchronous operation whose results are to be ignored.
        /// </param>
        public static async void IgnoreResult(this Task task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch
            {
                // ignore exceptions
            }
        }

        /// <summary>
        /// Ignore the results of an asynchronous operation allowing it to run and die silently in the background.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose results are to be ignored.
        /// </param>
        public static async void IgnoreResult(this ValueTask task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch
            {
                // ignore exceptions
            }
        }
    }

    /// <summary>
    /// Internal class for waiting for asynchronous operations that have a result.
    /// </summary>
    /// <typeparam name="TResult">
    /// The result type.
    /// </typeparam>
    public class SynchronousAwaiter<TResult>
    {
        /// <summary>
        /// The manual reset event signaling completion.
        /// </summary>
        private readonly ManualResetEvent manualResetEvent;

        /// <summary>
        /// The exception thrown by the asynchronous operation.
        /// </summary>
        private Exception exception;

        /// <summary>
        /// The result of the asynchronous operation.
        /// </summary>
        private TResult result;

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        public SynchronousAwaiter(Task<TResult> task)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        public SynchronousAwaiter(ValueTask<TResult> task)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task);
        }

        /// <summary>
        /// Gets a value indicating whether the operation is complete.
        /// </summary>
        public bool IsComplete => this.manualResetEvent.WaitOne(0);

        /// <summary>
        /// Synchronously get the result of an asynchronous operation.
        /// </summary>
        /// <returns>
        /// The result of the asynchronous operation.
        /// </returns>
        public TResult GetResult()
        {
            this.manualResetEvent.WaitOne();
            return this.exception != null ? throw this.exception : this.result;
        }

        /// <summary>
        /// Tries to synchronously get the result of an asynchronous operation.
        /// </summary>
        /// <param name="operationResult">
        /// The result of the operation.
        /// </param>
        /// <returns>
        /// The result of the asynchronous operation.
        /// </returns>
        public bool TryGetResult(out TResult operationResult)
        {
            if (this.IsComplete)
            {
                operationResult = this.exception != null ? throw this.exception : this.result;
                return true;
            }

            operationResult = default;
            return false;
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        private async void WaitFor(Task<TResult> task)
        {
            try
            {
                this.result = await task.ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        private async void WaitFor(ValueTask<TResult> task)
        {
            try
            {
                this.result = await task.ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }
    }

    /// <summary>
    /// Internal class for  waiting for  asynchronous operations that have no result.
    /// </summary>
    public class SynchronousAwaiter
    {
        /// <summary>
        /// The manual reset event signaling completion.
        /// </summary>
        private readonly ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        /// <summary>
        /// The exception thrown by the asynchronous operation.
        /// </summary>
        private Exception exception;

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        public SynchronousAwaiter(Task task, bool ignoreCancellation = false)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task, ignoreCancellation);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        public SynchronousAwaiter(ValueTask task, bool ignoreCancellation = false)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task, ignoreCancellation);
        }

        /// <summary>
        /// Gets a value indicating whether the operation is complete.
        /// </summary>
        public bool IsComplete => this.manualResetEvent.WaitOne(0);

        /// <summary>
        /// Synchronously get the result of an asynchronous operation.
        /// </summary>
        public void GetResult()
        {
            this.manualResetEvent.WaitOne();
            if (this.exception != null)
            {
                throw this.exception;
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        private async void WaitFor(Task task, bool ignoreCancellation)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        ///     The task.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        private async void WaitFor(ValueTask task, bool ignoreCancellation)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }
    }
}

편집:

작업에 Wait 메서드, 작업이 있습니다.Wait(): "약속"이 해결될 때까지 기다렸다가 계속 진행하여 약속이 동기화되도록 합니다.예:


async Task<String> MyAsyncMethod() { ... }

String mySyncMethod() {

    return MyAsyncMethod().Wait();
}

동기화 방법 생성기 라이브러리(너겟)를 사용하여 이 코드의 동기화된 버전을 생성할 수 있습니다.

다음과 같이 사용합니다.

[Zomp.SyncMethodGenerator.CreateSyncVersion]
public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

생성할 데이터GenerateCode동기식으로 호출할 수 있는 메서드입니다.

생성될 소스는 다음과 같습니다.

public string GenerateCode()
{
    string code = GenerateCodeService.GenerateCode();
    return code;
}

저는 비차단 접근 방식을 선호합니다.

            Dim aw1=GenerateCodeAsync().GetAwaiter()
            While Not aw1.IsCompleted
                Application.DoEvents()
            End While

만약 당신이 "RefreshList"라고 불리는 비동기 메서드를 가지고 있다면, 당신은 아래와 같은 비동기 메서드에서 그 비동기 메서드를 호출할 수 있습니다.

Task.Run(async () => { await RefreshList(); });

언급URL : https://stackoverflow.com/questions/22628087/calling-async-method-synchronously

반응형