我有一个静态方法,可以从任何地方调用。在执行期间,它将遇到Invoke
。显然,当从UI线程调用此方法时,它将会死锁。
这是一个复制品:
public static string Test(string text)
{
return Task.Run(() =>
{
App.Current.Dispatcher.Invoke(() => { } );
return text + text;
}).Result;
}
void Button_Click(object sender, RoutedEventArgs e) => Test();
我已经阅读了多个问题,并且喜欢@StephenCleary的10个答案(甚至还有一些与之相关的博客),但我无法理解如何实现以下目标:
与Test()
的行为最相似的是MessageBox.Show()
。
是否可以实现?
P.S。:为了保持问题简短,我没有附加我的各种async/await
尝试以及一个用于UI调用的尝试,但使用DoEvents一个看起来很糟糕。
答案 0 :(得分:1)
你不能。
即使这3个要求中只有2个不能同时实现 - "这种方法应该阻止呼叫者"与#34冲突;这种方法不应该冻结UI"。
您必须以某种方式使此方法异步(await
,回调)或使其在小块中可执行以仅在短时间内阻止UI,例如使用计时器来安排每个步骤。
重申你已经知道的内容 - 你可以阻止线程并在许多问题(如await works but calling task.Result hangs/deadlocks中讨论的同时回拨它。
答案 1 :(得分:0)
要实现MessageBox
所做的事情(但没有创建窗口),可以这样做:
public class Data
{
public object Lock { get; } = new object();
public bool IsFinished { get; set; }
}
public static bool Test(string text)
{
var data = new Data();
Task.Run(() =>
{
Thread.Sleep(1000); // simulate work
App.Current.Dispatcher.Invoke(() => { });
lock (data.Lock)
{
data.IsFinished = true;
Monitor.Pulse(data.Lock); // wake up
}
});
if (App.Current.Dispatcher.CheckAccess())
while (!data.IsFinished)
DoEvents();
else
lock (data.Lock)
Monitor.Wait(data.Lock);
return false;
}
static void DoEvents() // for wpf
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Func<object, object>(o =>
{
((DispatcherFrame)o).Continue = false;
return null;
}), frame);
Dispatcher.PushFrame(frame);
}
这个想法很简单:检查当前线程是否需要调用(UI线程),然后运行DoEvents
循环或阻塞线程。
Test()
。
它有效(虽然没有经过充分测试),但它很糟糕。我希望这会明确我的要求,如果有更好的“不,你不能这样做”,我仍然需要我的问题的答案;)