0

我有 MVP 应用程序 C#、.NET 4、WinForms。它使用通过 NamedPipe 与第三方应用程序通信的 Bridge 类。命令流程是这样的: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。还有其他可以执行的命令。最后,所有命令都到达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);
    }
}

我可以使用任务,Command在客户端类中等待吗?

4

2 回答 2

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. 使用 aTaskScheduler您可以Task在 UI 线程上启动 a (这不是您想要的,因为这会再次阻塞 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)); });
}
于 2013-08-01T21:50:57.477 回答
0

我锁定Command在客户端类。看起来它以这种方式完美地工作。没有阻塞的 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);
    }
}
于 2013-08-02T10:38:29.140 回答