은 때때로 참으로 UI 스레드 (예를 들어, 구문 강조, 맞춤법 검사로서의 당신 형 등)에 대한 몇 가지 비동기, 백그라운드 작업을 수행하는 데 필요합니다. 특정 (IMO, 고안된) 예제의 디자인 문제에 대해서는 질문하지 않을 것입니다. MVV 패턴을 사용해야 할 가능성이 높습니다. 그러나 UI 스레드를 응답 성있게 유지할 수는 있습니다.
보류중인 사용자 입력을 감지하고 주 메시지 루프에 응답하여 처리 우선 순위를 부여함으로써이를 수행 할 수 있습니다. 여기 당신이 해결하려고하는 작업을 기반으로 WinForms에서이를 수행하는 방법에 대한 완전하고 잘린 - 붙여 넣기 - 실행 예제가 있습니다.그냥 않는 await InputYield(token)
참고 :
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormsYield
{
static class Program
{
// a long-running operation on the UI thread
private static async Task LongRunningTaskAsync(Action<string> deliverText, CancellationToken token)
{
for (int i = 0; i < 10000; i++)
{
token.ThrowIfCancellationRequested();
await InputYield(token);
deliverText(await ReadLineAsync(token));
}
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// create some UI
var form = new Form { Text = "Test", Width = 800, Height = 600 };
var panel = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = true
};
form.Controls.Add(panel);
var button = new Button { Text = "Start", AutoSize = true };
panel.Controls.Add(button);
var inputBox = new TextBox
{
Text = "You still can type here while we're loading the file",
Width = 640
};
panel.Controls.Add(inputBox);
var textBox = new TextBox
{
Width = 640,
Height = 480,
Multiline = true,
ReadOnly = false,
AcceptsReturn = true,
ScrollBars = ScrollBars.Vertical
};
panel.Controls.Add(textBox);
// handle Button click to "load" some text
button.Click += async delegate
{
button.Enabled = false;
textBox.Enabled = false;
inputBox.Focus();
try
{
await LongRunningTaskAsync(text =>
textBox.AppendText(text + Environment.NewLine),
CancellationToken.None);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
button.Enabled = true;
textBox.Enabled = true;
}
};
Application.Run(form);
}
// simulate TextReader.ReadLineAsync
private static async Task<string> ReadLineAsync(CancellationToken token)
{
return await Task.Run(() =>
{
Thread.Sleep(10); // simulate some CPU-bound work
return "Line " + Environment.TickCount;
}, token);
}
//
// helpers
//
private static async Task TimerYield(int delay, CancellationToken token)
{
// yield to the message loop via a low-priority WM_TIMER message (used by System.Windows.Forms.Timer)
// https://web.archive.org/web/20130627005845/http://support.microsoft.com/kb/96006
var tcs = new TaskCompletionSource<bool>();
using (var timer = new System.Windows.Forms.Timer())
using (token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false))
{
timer.Interval = delay;
timer.Tick += (s, e) => tcs.TrySetResult(true);
timer.Enabled = true;
await tcs.Task;
timer.Enabled = false;
}
}
private static async Task InputYield(CancellationToken token)
{
while (AnyInputMessage())
{
await TimerYield((int)NativeMethods.USER_TIMER_MINIMUM, token);
}
}
private static bool AnyInputMessage()
{
var status = NativeMethods.GetQueueStatus(NativeMethods.QS_INPUT | NativeMethods.QS_POSTMESSAGE);
// the high-order word of the return value indicates the types of messages currently in the queue.
return status >> 16 != 0;
}
private static class NativeMethods
{
public const uint USER_TIMER_MINIMUM = 0x0000000A;
public const uint QS_KEY = 0x0001;
public const uint QS_MOUSEMOVE = 0x0002;
public const uint QS_MOUSEBUTTON = 0x0004;
public const uint QS_POSTMESSAGE = 0x0008;
public const uint QS_TIMER = 0x0010;
public const uint QS_PAINT = 0x0020;
public const uint QS_SENDMESSAGE = 0x0040;
public const uint QS_HOTKEY = 0x0080;
public const uint QS_ALLPOSTMESSAGE = 0x0100;
public const uint QS_RAWINPUT = 0x0400;
public const uint QS_MOUSE = (QS_MOUSEMOVE | QS_MOUSEBUTTON);
public const uint QS_INPUT = (QS_MOUSE | QS_KEY | QS_RAWINPUT);
[DllImport("user32.dll")]
public static extern uint GetQueueStatus(uint flags);
}
}
}
지금 당신은 당신이 여전히 배경에 텍스트로 채워되는 동안 사용자가 에디터의 내용을 수정하는 경우 수행하려고하는지 스스로에게 물어해야합니다. 여기서는 간단하게 버튼과 편집기 자체를 비활성화합니다 (나머지 UI는 액세스 가능하고 응답 가능합니다). 그러나 질문은 여전히 열려 있습니다. 또한이 샘플의 범위를 벗어나는 일부 취소 논리를 구현해야합니다.
당신 수 단지'...에서는 AppendText (await를 ... ReadLineAsync)'대신'File' API의 차단 변형을 사용. 그렇게하면 수동으로 컨텍스트에 저장하거나 게시 할 필요가 없습니다. – JSteward
@JSteward 시도한 값 .AppendText (sr.ReadLineAsync()를 기다림)하지만 lamba 표현식에만 적용될 수 있습니다. – ppk
**'async' ** lamda 표현식에만 적용 할 수 있다는 것을 의미한다고 생각합니다. 질문에서 코드를 업데이트 할 수 있습니까? – JSteward