我有MVP应用程序C#,.NET 4,WinForms。它使用Bridge类,通过NamedPipe与第三方应用程序通信。 命令流如下:View→Presenter→Manager→Bridge→Client 并以相反的顺序返回。 View已准备好进行多任务处理。我通过结果上升事件在Manager中拆分反向链,但它没有帮助。
// View class
public void AccountInfo_Clicked() { presenter.RequestAccountInfo(); }
public void UpdateAccountInfo(AccountInfo info)
{
if (pnlInfo.InvokeRequired)
pnlInfo.BeginInvoke(new InfoDelegate(UpdateAccountInfo), new object[] {info});
else
pnlInfo.Update(info);
}
// Presenter class
public void RequestAccountInfo() { manager.RequestAccountInfo(); }
private void Manager_AccountInfoUpdated(object sender, AccountInfoEventArgs e)
{
view.UpdateAccountInfo(e.AccountInfo);
}
// Manager class
public void RequestAccountInfo()
{
AccountInfo accountInfo = bridge.GetAccountInfo();
OnAccountInfoUpdated(new AccountInfoEventArgs(accountInfo));
}
// Bridge class
public AccountInfo GetAccountInfo() { return client.GetAccountInfo(); }
// Client class
public AccountInfo GetAccountInfo()
{
string respond = Command("AccountInfo");
return new AccountInfo(respond);
}
private string Command(string command)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
我想在命令处理期间解冻UI。还有其他可以执行的命令。最后,所有命令都在Client中到达Command(string command)
方法。
我试图通过使用task和ContinueWith来破坏Manager中的链,但是导致管道无法连接。原因是客户端不是线程安全的。
// Manager class
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
我的问题是:在哪里使用Task,ContinueWith以及在哪里锁定?
我认为我只能锁定Command(string command)
,因为它是最终的方法。
private string Command(string command)
{
lock (pipeLock)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}
我可以使用Task,在Client类中等待Command
吗?
答案 0 :(得分:0)
我认为您遇到的问题是bridge.GetAccountInfo()
正在尝试从UI本身提取信息 - 因此是UI线程。这段代码
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
正在尝试从后台线程池线程执行bridge.GetAccountInfo()
方法(访问UI)。
我的第一个问题是拨打bridge.GetAccountInfo()
的费用是多少?如果它不昂贵,那么将这方面的工作纳入多线程是没有意义的。如果它很昂贵,你将不得不考虑一种方法来使这个操作线程安全(没有更多的信息我不能建议)。
要做的另一件事是评估转移到WCF的费用。这为你处理大多数同步问题......对不起,我无法提供更多帮助。我在阅读你的上一条评论之前写了上述内容。
我希望这有一些用处。
除此之外:需要注意的是SynchronizationContext
。使用TaskScheduler
,您可以在UI线程上启动Task
(这不是您想要的,因为这将再次阻止UI - 但是,这可以很好地了解何时报告[in。 NET 4.0]。要在UI线程上启动上面的代码,你可以做到
public void RequestAccountInfo()
{
var task = Task<AccountInfo>.Factory.StartNew(() =>
bridge.GetAccountInfo(),
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}
答案 1 :(得分:0)
我将Command
锁定在Client类中。它似乎以这种方式完美地运作。没有阻止UI,没有管道错误。我锁定pipeName
,因为View的每个副本都使用唯一的管道名称。
我将Task<Type>
,ContinueWith
应用于Manager
类中的所有命令。
// Manager class
public void RequestSomeInfo()
{
var task = Task<SomeInfo>.Factory.StartNew(() => bridge.GetSomeInfo());
task.ContinueWith(t => { OnInfoUpdated(new InfoEventArgs(t.Result)); });
}
// Client class
private string Command(string command)
{
lock (pipeName)
{
var pipe = new ClientPipe(pipeName);
pipe.Connect();
return pipe.Command(command);
}
}