Search code examples
c#.netlockingtaskmvp

Where to Break the Chain with Task, ContinueWith, Lock


I have MVP application C#, .NET 4, WinForms. It uses Bridge class which communicate with third party app via NamedPipe. The command flow is like this: View → Presenter → Manager → Bridge → Client And back in the reverse order. View is prepared for multitasking. I split reverse chain in Manager by rising event with the result, but it doesn't help.

// 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);
}

I want to unfreeze the UI during command processing. There are also other commands that can be executed. Finally all commands reach Command(string command) method in Client.

I tried to break the chain in Manager by using task and ContinueWith but it results to pipe failing to connect. The reason is that client is not thread safe.

// Manager class
public void RequestAccountInfo()
{
    var task = Task<AccountInfo>.Factory.StartNew(() => bridge.GetAccountInfo());
    task.ContinueWith(t => { OnAccountInfoUpdated(new AccountInfoEventArgs(t.Result)); });
}

My question is: Where to use Task, ContinueWith and where to Lock?

I assume I can lock only Command(string command) because it is the ultimate method.

private string Command(string command)
{
    lock (pipeLock)
    {
        var pipe = new ClientPipe(pipeName);
        pipe.Connect();
        return pipe.Command(command);
    }
}

Can I use Task, Wait in Command in Client class?


Solution

  • I locked Command in Client class. It appears that it works perfectly in that way. No blocking UI, no pipe errors. I lock on pipeName because each copy of View is using a unique pipe name.

    I applied Task<Type>, ContinueWith to all commands in Manager class.

    // 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);
        }
    }