내용 보기
작성자
관리자 (IP : 192.168.0.1)
날짜
2022-12-28 00:14
제목
[C#] [스크랩] 직접 만들어 보는 SynchronizationContext
SynchronizationContext를 소개했는데요, 이번 글에서는 우리가 직접 SynchronizationContext를 만들어보겠습니다. ^^ (2개 만들어 볼 것입니다.) class MyWinFormsSynchronizationContext : SynchronizationContext
{
private readonly Control _proxy;
public MyWinFormsSynchronizationContext(Control control)
{
_proxy = control;
}
public override void Post(SendOrPostCallback d, object? state)
{
_proxy.BeginInvoke(() =>
{
d(state);
});
}
public override void Send(SendOrPostCallback d, object? state)
{
_proxy.Invoke(() =>
{
d(state);
});
}
}
public partial class Form1 : Form { MyWinFormsSynchronizationContext _syncContext; public Form1() { _syncContext = new MyWinFormsSynchronizationContext(this); SynchronizationContext.SetSynchronizationContext(_syncContext); InitializeComponent(); } }
public partial class Form1 : Form
{
// ...[생략]...
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
this.button1.Text = "TEST"; // MyWinFormsSynchronizationContext를 통해 UI 스레드에서 실행
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
}
private void button2_Click(object sender, EventArgs e) { SynchronizationContext? ctx = SynchronizationContext.Current; // MyWinFormsSynchronizationContext Task.Run(() => { ctx?.Post((arg1) => { this.button2.Text = "TEST"; // MyWinFormsSynchronizationContext를 통해 UI 스레드에서 실행 }, null); }); }
C# - 직접 만들어 보는 TaskScheduler 실습 (SingleThreadTaskScheduler) ; https://www.sysnet.pe.kr/2/0/13188
public override void Post(SendOrPostCallback d, object? state) { System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Post"); _proxy.BeginInvoke(() => { System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: User Work"); d(state); } ); }
private void button2_Click(object sender, EventArgs e) { SynchronizationContext? ctx = SynchronizationContext.Current; Task.Run(() => { ctx?.Post((arg1) => { this.button2.Text = "TEST"; }, null); }); } /* 출력 결과 9: Post 1: User Work */
private void button1_Click(object sender, EventArgs e) { Task.Factory.StartNew(() => { this.button1.Text = "TEST"; // UI 스레드에서 실행 }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } 1: Post 1: User Work
public override void Post(SendOrPostCallback d, object? state)
{
if (_proxy.InvokeRequired)
{
_proxy.BeginInvoke(() =>
{
d(state);
}
);
}
else
{
d(state); // SynchronizationContextTaskScheduler인 경우, 직접 호출
}
}
// SynchronizationContextTaskScheduler protected override void QueueTask(Task task) { // 아래의 코드 대신 그냥 "s_postCallback(task);"로 호출하는 코드로 바꾼다면? m_synchronizationContext.Post(s_postCallback, (object)task); }
private void button1_Click(object sender, EventArgs e) { var ctx = TaskScheduler.FromCurrentSynchronizationContext(); Task.Run(() => { Task.Factory.StartNew(() => { System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: StartNew"); this.button1.Text = "TEST"; }, CancellationToken.None, TaskCreationOptions.None, ctx); }); }
private async void button3_Click(object sender, EventArgs e)
{
await Task.Delay(1000);
System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: await");
this.button1.Text = "TEST";
}
C# - async/await 그리고 스레드 (3) Task.Delay 재현 ; https://www.sysnet.pe.kr/2/0/13060
private void button3_Click(object sender, EventArgs e)
{
Form1.<button3_Click>d__4 <button3_Click>d__ = new Form1.<button3_Click>d__4();
<button3_Click>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<button3_Click>d__.<>4__this = this;
<button3_Click>d__.sender = sender;
<button3_Click>d__.e = e;
<button3_Click>d__.<>1__state = -1;
<button3_Click>d__.<>t__builder.Start<Form1.<button3_Click>d__4>(ref <button3_Click>d__);
}
public static AsyncVoidMethodBuilder Create()
{
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
if (synchronizationContext != null)
{
synchronizationContext.OperationStarted();
}
return new AsyncVoidMethodBuilder
{
_synchronizationContext = synchronizationContext
};
}
async/await 사용 시 hang 문제가 발생하는 경우 - 두 번째 이야기 ; https://www.sysnet.pe.kr/2/0/10801 비동기 메서드 내에서 await 시 ConfigureAwait 호출 의미 ; https://www.sysnet.pe.kr/2/0/11418 WebClient 타입의 ...Async 메서드 호출은 왜 await + 동기 호출 시 hang 현상이 발생할까요? ; https://www.sysnet.pe.kr/2/0/11419 C# - Task.Yield 사용법 ; https://www.sysnet.pe.kr/2/0/12241
// 1개의 전담 스레드를 만들어 SynchronizationContext의 요청을 처리 class SingleThreadSynchronizationContext : SynchronizationContext { static Thread _thread; static BlockingCollection<WorkItem> _workItems; class WorkItem { SendOrPostCallback _callback; public SendOrPostCallback Callback => _callback; object? _state; public object? State => _state; internal WorkItem(SendOrPostCallback callback, object? state) { _callback = callback; _state = state; } } static SingleThreadSynchronizationContext() { _workItems = new BlockingCollection<WorkItem>(); _thread = new Thread(threadFunc); _thread.IsBackground = true; _thread.Start(); } static void threadFunc() { while (true) { var item = _workItems.Take(); item.Callback(item.State); } } public override void Post(SendOrPostCallback d, object? state) { _workItems.Add(new WorkItem(d, state)); } public override void Send(SendOrPostCallback d, object? state) { throw new NotSupportedException(); } }
public partial class Form1 : Form
{
SingleThreadSynchronizationContext _singleContext;
public Form1()
{
_singleContext = new SingleThreadSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(_singleContext);
InitializeComponent();
}
private async void button3_Click(object sender, EventArgs e)
{
await Task.Delay(1000);
// 이 코드는 SingleThreadSynchronizationContext가 제공하는 단일 스레드에서 실행
System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: await");
// 따라서, UI 요소는 접근할 수 없으므로 그런 경우에는 Control.Invoke/BeginInvoke로 호출
this.Invoke(() =>
{
this.button1.Text = "TEST";
});
}
}
11: await
private async void button3_Click(object sender, EventArgs e) { await Task.Delay(1000); System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: await"); await Task.Delay(1000); System.Diagnostics.Trace.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: await"); }
11: await 10: await // 한 번 더 버튼을 눌러봐도, 11: await 15: await
class SingleThreadSynchronizationContext : SynchronizationContext
{
// ...[생략]...
static void threadFunc()
{
SynchronizationContext.SetSynchronizationContext(new SingleThreadSynchronizationContext());
while (true)
{
var item = _workItems.Take();
item.Callback(item.State);
}
}
// ...[생략]...
}
|
출처1
https://www.sysnet.pe.kr/2/0/13191
출처2