VS2012를 얻었고에 대한 핸들을 얻으려고했습니다 async
.
차단 소스에서 일부 값을 가져 오는 메소드가 있다고 가정 해 봅시다. 메소드 호출자가 차단하고 싶지 않습니다. 값이 도착하면 호출되는 콜백을 수행하는 메소드를 작성할 수 있지만 C # 5를 사용하고 있으므로 호출자가 콜백을 처리하지 않아도되도록 메소드를 비동기로 설정하기로 결정합니다.
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
다음은이를 호출하는 예제 메소드입니다. 경우 PromptForStringAsync
비동기 아니었다,이 방법은 콜백 내에서 콜백을 중첩 필요합니다. 비동기를 사용하면이 방법을 매우 자연스럽게 작성할 수 있습니다.
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
여태까지는 그런대로 잘됐다. 문제는 GetNameAsync를 호출 할 때입니다 .
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
요점은 GetNameAsync
비동기 적이라는 것입니다. MainWorkOfApplicationIDontWantBlocked ASAP로 돌아가서 GetNameAsync가 백그라운드에서 작동하도록하기 때문에 차단 하고 싶지 않습니다 . 그러나이 방법으로 호출하면 컴파일러 경고가 표시됩니다 GetNameAsync
.
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
“통화가 완료되기 전에 현재 메소드의 실행이 계속된다”는 것을 완벽하게 알고 있습니다. 그것이 비동기 코드 의 요점 입니다. 맞습니까?
경고없이 컴파일하는 코드를 선호하지만, 코드가 의도 한대로 정확하게 수행하므로 여기에서 “수정”할 것은 없습니다. 반환 값을 저장하여 경고를 제거 할 수 있습니다 GetNameAsync
.
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
그러나 이제 불필요한 코드가 있습니다. Visual Studio는 정상적인 “사용하지 않은 값”경고를 표시하지 않기 때문에이 불필요한 코드를 작성해야한다는 것을 이해하고있는 것 같습니다.
비동기가 아닌 메서드에 GetNameAsync를 래핑하여 경고를 제거 할 수도 있습니다.
public static Task GetNameWrapper()
{
return GetNameAsync();
}
그러나 그것은 훨씬 더 불필요한 코드입니다. 따라서 불필요한 경고가 필요하지 않거나 허용하지 않는 코드를 작성해야합니다.
여기에 잘못된 비동기 사용에 관한 것이 있습니까?
답변
실제로 결과가 필요하지 않으면 GetNameAsync
의 서명을 변경하여 void
다음 을 반환 할 수 있습니다 .
public static async void GetNameAsync()
{
...
}
관련 질문에 대한 답변을 참조하십시오 :
보이드 반환과 작업 반환의 차이점은 무엇입니까?
최신 정보
결과가 필요 GetNameAsync
하면를 반환하도록 변경할 수 있습니다 Task<string>
.
public static async Task<string> GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
string lastname = await PromptForStringAsync("Enter your last name: ");
return firstname + lastname;
}
다음과 같이 사용하십시오.
public static void DoStuff()
{
Task<string> task = GetNameAsync();
// Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
Task anotherTask = task.ContinueWith(r => {
Console.WriteLine(r.Result);
});
MainWorkOfApplicationIDontWantBlocked();
// OR wait for the result AFTER
string result = task.Result;
}
답변
이 토론에 꽤 늦었지만 #pragma
전 처리기 지시문 을 사용하는 옵션도 있습니다 . 여기에 일부 조건에서 명시 적으로 기다리고 싶지 않은 비동기 코드가 있으며 나머지 사람들처럼 경고 및 사용하지 않는 변수를 싫어합니다.
#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014
은 "4014"
이 MSDN 페이지에서 제공 : 컴파일러 경고 (수준 1) CS4014 .
https://stackoverflow.com/a/12145047/928483에서 @ ryan-horath의 경고 / 응답도 참조 하십시오 .
대기하지 않는 비동기 호출 중에 발생한 예외는 유실됩니다. 이 경고를 없애려면 비동기 호출의 작업 반환 값을 변수에 할당해야합니다. 이렇게하면 throw 된 예외에 액세스 할 수 있으며 이는 반환 값에 표시됩니다.
C # 7.0 업데이트
C # 7.0은 새로운 기능인 변수 삭제 : Discards-C # Guide를 추가하여 이와 관련하여 도움을 줄 수 있습니다.
_ = SomeMethodAsync();
답변
나는 사용하지 않는 변수에 작업을 할당하거나 void를 반환하도록 메소드 서명을 변경하는 솔루션을 좋아하지 않습니다. 전자는 불필요한 비 직관적 인 코드를 생성하지만 후자는 인터페이스를 구현하거나 반환 된 작업을 사용하려는 함수를 다시 사용하는 경우 가능하지 않을 수 있습니다.
내 해결책은 아무것도하지 않는 DoNotAwait ()라는 Task의 확장 메서드를 만드는 것입니다. 이렇게하면 ReSharper 등의 모든 경고가 표시되지 않을뿐만 아니라 코드를보다 이해하기 쉽게하고 코드의 미래 관리자에게 전화를 기다리지 않을 것이라는 사실을 알려줍니다.
확장 방법 :
public static class TaskExtensions
{
public static void DoNotAwait(this Task task) { }
}
용법:
public static void DoStuff()
{
GetNameAsync().DoNotAwait();
MainWorkOfApplicationIDontWantBlocked();
}
추가하기 위해 편집 : 이것은 확장 방법이 아직 시작되지 않은 경우 작업을 시작하는 Jonathan Allen의 솔루션과 유사하지만 발신자의 의도가 완전히 명확하도록 단일 목적 기능을 선호합니다.
답변
async void
나쁘다!
- 무효 반환과 작업 반환의 차이점은 무엇입니까?
- https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
내가 제안 Task
하는 것은 익명의 방법을 통해 명시 적으로 실행하는 것입니다 …
예 :
public static void DoStuff()
{
Task.Run(async () => GetNameAsync());
MainWorkOfApplicationIDontWantBlocked();
}
또는 차단을 원하면 익명 메소드를 기다릴 수 있습니다.
public static void DoStuff()
{
Task.Run(async () => await GetNameAsync());
MainWorkOfApplicationThatWillBeBlocked();
}
그러나 GetNameAsync
메소드가 UI 또는 UI 바운드와 상호 작용 해야하는 경우 (WINRT / MVVM, 당신을보고 있습니다) 약간 더 재미있어집니다 =)
UI 디스패처에 대한 참조를 다음과 같이 전달해야합니다.
Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
그런 다음 비동기 메소드에서 디스패처가 생각한 UI 또는 UI 바운드 요소와 상호 작용해야합니다 …
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; });
답변
이것이 내가 현재하고있는 일입니다.
SomeAyncFunction().RunConcurrently();
경우 RunConcurrently
로 정의된다 …
/// <summary>
/// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running.
/// </summary>
/// <param name="task">The task to run.</param>
/// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks>
public static void RunConcurrently(this Task task)
{
if (task == null)
throw new ArgumentNullException("task", "task is null.");
if (task.Status == TaskStatus.Created)
task.Start();
}
답변
이 경고에 대한 Microsoft 기사에 따르면 반환 된 작업을 변수에 지정하면 해결할 수 있습니다. 아래는 Microsoft 예제에서 제공된 코드의 번역입니다.
// To suppress the warning without awaiting, you can assign the
// returned task to a variable. The assignment doesn't change how
// the program runs. However, the recommended practice is always to
// await a call to an async method.
// Replace Call #1 with the following line.
Task delayTask = CalledMethodAsync(delay);
이렇게하면 ReSharper에서 “로컬 변수를 사용하지 않습니다”라는 메시지가 나타납니다.
답변
여기 간단한 해결책이 있습니다.
public static class TasksExtensions
{
public static void RunAndForget(this Task task)
{
}
}
문안 인사
