내용 보기
작성자
관리자 (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