SynchronizationContext Send() 应该是同一个线程?
SynchronizationContext Send() supposed to be the same thread?
我有这种情况,我尝试在创建事件的同一线程上处理事件。这通常在 UiThread 中完成,但我不是从 UiThread 开始的。我基本上按照以下步骤进行了一些测试。我遗漏了一些细节。我不太确定这是否应该像我认为的那样。
首先我查看当前线程的Id
var myThreadId = Thread.CurrentThread.ManagedThreadId;
我创建了一个 SynchronizationContext 并将其设置为当前
var _context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(_context);
然后我向上下文发送一些动作(我们现在在另一个线程上)
_context.Send(x => _action(sender, e), null);
在此操作中,我再次检查 ThreadId
Assert.Equal(myThreadId, Thread.CurrentThread.ManagedThreadId);
这失败了。我不应该再回到我原来的话题吗?
如果您创建一个新的 SynchronizationContext
,它将始终包装线程池并且永远不会在 UI 线程上执行 Send
或 Post
。
The SynchronizationContext class is a base class that provides a
free-threaded context with no synchronization.
例如;
void Button_Click(object sender, EventArgs e)
{
var context = SynchronizationContext.Current;
// this is executred on the UI thread.
context.Send(() =>
{
// this is also executed on the UI thread.
});
Task.Run(() =>
{
// this is executed on a worker thread
context.Send(() =>
{
// this is still executed on the UI thread!
});
}
// what you are doing will always execute on a worker thread.
var myNewContext = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(myNewContext);
myNewContext.Send(() =>
{
// this will run on a worker thread.
}
}
进一步阅读
Parallel Computing - It's All About the SynchronizationContext
创建一个新的 SynchronizationContext
并使用 Send
或 Post
与同步委托调用完全相同,就像您自己做一样。代码相当简单(取自the source):
public virtual void Send(SendOrPostCallback d, Object state)
{
d(state);
}
您正在尝试模仿自定义上下文的操作,例如 DispatcherSynchronizationContext
,它知道 WPF 的 UI 消息循环线程。这种行为不会发生在这里。
如果您来自 UI 线程,则需要捕获上下文并将其传递。
您可以在 DispatcherSynchronizationContext
中更清楚地看到,哪些队列使用 Dispatcher
class:
为 UI 工作
/// <summary>
/// Synchronously invoke the callback in the SynchronizationContext.
/// </summary>
public override void Send(SendOrPostCallback d, Object state)
{
// Call the Invoke overload that preserves the behavior of passing
// exceptions to Dispatcher.UnhandledException.
if(BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() &&
_dispatcher.CheckAccess())
{
// Same-thread, use send priority to avoid any reentrancy.
_dispatcher.Invoke(DispatcherPriority.Send, d, state);
}
else
{
// Cross-thread, use the cached priority.
_dispatcher.Invoke(_priority, d, state);
}
}
我有这种情况,我尝试在创建事件的同一线程上处理事件。这通常在 UiThread 中完成,但我不是从 UiThread 开始的。我基本上按照以下步骤进行了一些测试。我遗漏了一些细节。我不太确定这是否应该像我认为的那样。
首先我查看当前线程的Id
var myThreadId = Thread.CurrentThread.ManagedThreadId;
我创建了一个 SynchronizationContext 并将其设置为当前
var _context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(_context);
然后我向上下文发送一些动作(我们现在在另一个线程上)
_context.Send(x => _action(sender, e), null);
在此操作中,我再次检查 ThreadId
Assert.Equal(myThreadId, Thread.CurrentThread.ManagedThreadId);
这失败了。我不应该再回到我原来的话题吗?
如果您创建一个新的 SynchronizationContext
,它将始终包装线程池并且永远不会在 UI 线程上执行 Send
或 Post
。
The SynchronizationContext class is a base class that provides a free-threaded context with no synchronization.
例如;
void Button_Click(object sender, EventArgs e)
{
var context = SynchronizationContext.Current;
// this is executred on the UI thread.
context.Send(() =>
{
// this is also executed on the UI thread.
});
Task.Run(() =>
{
// this is executed on a worker thread
context.Send(() =>
{
// this is still executed on the UI thread!
});
}
// what you are doing will always execute on a worker thread.
var myNewContext = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(myNewContext);
myNewContext.Send(() =>
{
// this will run on a worker thread.
}
}
进一步阅读
Parallel Computing - It's All About the SynchronizationContext
创建一个新的 SynchronizationContext
并使用 Send
或 Post
与同步委托调用完全相同,就像您自己做一样。代码相当简单(取自the source):
public virtual void Send(SendOrPostCallback d, Object state)
{
d(state);
}
您正在尝试模仿自定义上下文的操作,例如 DispatcherSynchronizationContext
,它知道 WPF 的 UI 消息循环线程。这种行为不会发生在这里。
如果您来自 UI 线程,则需要捕获上下文并将其传递。
您可以在 DispatcherSynchronizationContext
中更清楚地看到,哪些队列使用 Dispatcher
class:
/// <summary>
/// Synchronously invoke the callback in the SynchronizationContext.
/// </summary>
public override void Send(SendOrPostCallback d, Object state)
{
// Call the Invoke overload that preserves the behavior of passing
// exceptions to Dispatcher.UnhandledException.
if(BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() &&
_dispatcher.CheckAccess())
{
// Same-thread, use send priority to avoid any reentrancy.
_dispatcher.Invoke(DispatcherPriority.Send, d, state);
}
else
{
// Cross-thread, use the cached priority.
_dispatcher.Invoke(_priority, d, state);
}
}