在哪里打破任务链,ContinueWith,锁定

时间:2013-08-01 21:13:47

标签: c# .net locking task mvp

我有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吗?

2 个答案:

答案 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);
    }
}