비동기 메서드를 동기적으로 호출하는 중
나는 있습니다.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
메인 스레드를 차단하여 비동기 코드의 나머지가 실행되지 않도록 합니다.이러한 문제가 발생하지 않도록 다음 옵션을 사용할 수 있습니다.
스레드 풀 스레드에서 비동기 메서드를 명시적으로 실행하고 완료될 때까지 기다립니다.
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.net 태그가 지정되어 있으며, 다음과 같은 의견도 남겨두었습니다.
저는 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
'programing' 카테고리의 다른 글
Firebase 아키텍처 x86_64에 대한 정의되지 않은 기호 (0) | 2023.06.05 |
---|---|
전자 메일 보내기 의도 (0) | 2023.05.31 |
Bash에서 여러 줄로 문자열을 분할하는 방법은 무엇입니까? (0) | 2023.05.31 |
단순 UIA 경고 보기 추가 (0) | 2023.05.31 |
VBA(Excel) 루프 없이 전체 어레이 초기화 (0) | 2023.05.31 |