C# 如何等待wait/async方法完成

C# 如何等待wait/async方法完成,c#,multithreading,task-parallel-library,async-await,C#,Multithreading,Task Parallel Library,Async Await,我有以下异步方法: private async void ProcessSearch() { // get catalogs on first search if (_invoiceTypes == null && _invoiceAccounts == null) { var confWcf = new Data.ConfigurationWCF(); _invoiceTypes = await confWcf.GetIn

我有以下异步方法:

private async void ProcessSearch()
{
    // get catalogs on first search
    if (_invoiceTypes == null && _invoiceAccounts == null)
    {
        var confWcf = new Data.ConfigurationWCF();
        _invoiceTypes = await confWcf.GetInvoiceTypesAsync(MainForm.State.Entity);
        _invoiceAccounts = await confWcf.GetInvoiceAccountsAsync(MainForm.State.Entity);
        confWcf.Dispose();
    }

    var seekWcf = new DataSeekWCF();
    _ds = await seekWcf.SearchInvoiceAdminAsync(new Guid(cboEmployer.Value.ToString()), new Guid(cboGroup.Value.ToString()), txtSearchInvoiceNumber.Text, chkSearchLike.Checked, txtSearchFolio.Text, Convert.ToInt32(txtYear.Value));
    seekWcf.Dispose();

    if (_ds != null)
    {
        SetupInvoiceGrid();
    }
}
我不想执行SetupInvoiceGrid,直到\u invoiceTypes、\u invoiceAccounts和\u ds完成

有线索吗?我做得对吗?我应该使用任务而不是等待吗


我已经想出了这个代码,它看起来很有效,对我来说很好,但不知道它是否正确:

private void btnSearch_Click(object sender, EventArgs e)
{
    lock (lockObj)
    {
        if (_isBusy)
            return;
        else
            _isBusy = true;
    }

    ShowPleaseWait(Translate("Searching data. Please wait..."));
        if (_invoiceTypes == null && _invoiceAccounts == null)
        {
            var t = GetCatalogs();
            t.ContinueWith(t2 =>
            {
                if (t.IsCompleted) ProcessSearch();
            });
        }
        else
        {
            ProcessSearch();
        }
}

private async Task GetCatalogs()
{
    // get catalogs on first search
    Data.ConfigurationWCF confWcf = new Data.ConfigurationWCF();
    var task1 = confWcf.GetInvoiceTypesAsync(1);
    var task2 = confWcf.GetInvoiceAccountsAsync(1);
    confWcf.Dispose();

    await Task.WhenAll(task1, task2);

    _invoiceTypes = task1.Result;
    _invoiceAccounts = task2.Result;

    if (_invoiceTypes != null)
    {
        cboInvoiceType.DataSource = _invoiceTypes.Tables["invoice_types"];
        cboInvoiceType.DisplayMember = "description";
        cboInvoiceType.ValueMember = "code";
    }

}

private async void ProcessSearch()
{
    var seekWcf = new Data.SeekWCF();
    _ds = await seekWcf.SearchInvoiceAdminAsync(new Guid(cboEmployer.Value.ToString()), new Guid(cboGroup.Value.ToString()), txtSearchInvoiceNumber.Text, chkSearchLike.Checked, txtSearchFolio.Text, Convert.ToInt32(txtYear.Value));
    seekWcf.Dispose();

    if (_ds != null)
    {
        SetupInvoiceGrid();
    }
    HidePleaseWait();
}
我回答了关于如何处理
ProcessSearchAsync
本身的完成的原始(?)问题

为了并行运行任务(如注释中所述),这里修改了您的代码,由于
invoiceTypes==null
\u invoiceAccounts==null
检查,它变得有点复杂。请注意,以下检查的实现方式略微改变了逻辑(以前,只有当_invoiceTypes和_invoiceAccounts都为null时,才会执行WCF调用-如果其中只有一个为null怎么办?)

private async Task ProcessSearchAsync()
{
Data.ConfigurationWCF confWcf=新数据.ConfigurationWCF();
任务t1;
任务t2;
如果(_invoiceTypes==null)
t1=confWcf.GetInvoiceTypesAsync(MainForm.State.Entity);
其他的
{
var tsc1=新任务完成源();
t1=tsc1.任务;
tsc1.SetResult(_invoiceTypes);
}
如果(_invoiceAccounts==null)
t2=confWcf.GetInvoiceAccountsAsync(MainForm.State.Entity);
其他的
{
var tsc2=新任务完成源();
t2=tsc2.任务;
tsc2.设置结果(_发票账户);
}
DataSeekWCF seekWcf=新的DataSeekWCF();
任务t3=seekWcf.SearchInvoiceAdminAsync(新Guid(cboEmployer.Value.ToString()),新Guid(cboGroup.Value.ToString()),txtSearchInvoiceNumber.Text,chkSearchLike.Checked,txtSearchFolio.Text,Convert.ToInt32(txtYear.Value));
wait Task.WhenAll(新任务[]{t1,t2,t3});
_invoiceTypes=t1.结果;
_发票账户=t2。结果;
ds=t3.结果;
如果(_ds!=null)
{
SetupInvoiceGrid();
}
confWcf.Dispose();
seekWcf.Dispose();
}

对现有内容的细微更改将满足您的需求。你可以开始新的任务,然后做其他事情,然后在继续之前等待。正如@nosertio所指出的,下面的代码片段还没有准备好生产,因为我没有检查错误条件(比如
null
references等等)。关键是,您可以简洁优雅地并行完成这些事情,而不必使用太多的Tasks API。我所做的一个调整值得指出的是,您希望将对
Dispose
的调用移动到continuation中(即,在所有
wait
s之后)因为如果您在调用*Async方法之后立即尝试
Dispose
,您很有可能在得到响应的中途杀死您的WCF客户端,
等待
可能会抛出异常(我没有捕捉到)

private async void ProcessSearchAsync()
{
Data.ConfigurationWCF confWcf=新数据.ConfigurationWCF();
任务t1;
任务t2;
//在第一次搜索时获取目录
如果(_invoiceTypes==null&&u invoiceAccounts==null)
{
t1=confWcf.GetInvoiceTypesAsync(MainForm.State.Entity);
t2=confWcf.GetInvoiceAccountsAsync(MainForm.State.Entity);
}
DataSeekWCF seekWcf=新的DataSeekWCF();
任务t3=seekWcf.SearchInvoiceAdminAsync(新Guid(cboEmployer.Value.ToString()),新Guid(cboGroup.Value.ToString()),txtSearchInvoiceNumber.Text,chkSearchLike.Checked,txtSearchFolio.Text,Convert.ToInt32(txtYear.Value));
_发票类型=等待t1;
_发票账户=等待t2;
_ds=等待t3;
如果(_ds!=null)
{
SetupInvoiceGrid();
}
confWcf.Dispose();
seekWcf.Dispose();
}

您是否尝试过运行此程序以查看其是否有效?我觉得很好,这很管用。。但我想用SearchInvoiceAdminAsync并行运行GetInvoiceTypesAsync和GetInvoiceAccountsAsync。因此,这3个方法运行pararell,但我无法执行SetupInvoiceGrid,直到它们全部完成。例如,我希望与SearchInvoiceAdminAsync并行运行GetInvoiceTypesAsync和GetInvoiceAccountsAsync。因此,3个方法运行pararell,但在所有方法完成之前,我无法执行SetupInvoiceGrid。ProcessSearchAsync与我使用的方法相同,只是返回Task而不是void?不,
ProcessSearchAsync
只是在声明它时按惯例命名它的方式。我将在答案中展示如何并行运行任务。不,它们不是并行运行的:_invoiceTypes=await t1_发票账户=等待t2_ds=等待t3,而是以串行方式:首先等待t1,然后等待t2,然后等待t3。试试这个:
wait Task.WhenAll(新任务[]{t1,t2,t3})_invoiceTypes=t1.结果_发票账户=t2。结果;ds=t3.结果
它甚至比这更复杂,因为像
invoiceTypes==null
\u invoiceAccounts==null
这样的检查是您最初使用的。看新版。你认为我刚才编辑的关于这个问题的更新代码可以吗?谢谢你是的,我觉得你的更新看起来基本上是对的。我认为使用
Task.whalll
会使语法比实际需要的更详细,但应该可以。对不起,这是错误的。首先,如果
\u invoiceTypes |=null
\u invoiceAccounts |=null
,它将出现故障,因为
t1
t2
将保持未初始化状态。第二,任务t1、t2、t3不是并行运行的,而是按顺序运行的。第三,如果_invoiceTypes和_invoiceAccounts成员在开始时不为空,那么是否有重用它们的目标?@avo的答案对我来说似乎更正确。@Noseratio是的,你(部分)是对的。我对我的答案编辑了几次,其中一次我的修改中有一两句话说“你想做更多的错误检查”,o
private async Task ProcessSearchAsync()
{

    Data.ConfigurationWCF confWcf = new Data.ConfigurationWCF();
    Task</*typeof _invoiceTypes*/> t1;
    Task</*typeof _invoiceAccounts*/> t2;

    if (_invoiceTypes == null)
        t1 = confWcf.GetInvoiceTypesAsync(MainForm.State.Entity);
    else
    {
        var tsc1 = new TaskCompletionSource</*typeof _invoiceTypes*/>();
        t1 = tsc1.Task;
        tsc1.SetResult(_invoiceTypes);
    }

    if ( _invoiceAccounts == null )
        t2 = confWcf.GetInvoiceAccountsAsync(MainForm.State.Entity);
    else
    {
        var tsc2 = new TaskCompletionSource</*typeof _invoiceAccounts*/>();
        t2 = tsc2.Task;
        tsc2.SetResult(_invoiceAccounts);
    }


    DataSeekWCF seekWcf = new DataSeekWCF();
    Task</*typeof _ds*/> t3 = seekWcf.SearchInvoiceAdminAsync(new Guid(cboEmployer.Value.ToString()), new Guid(cboGroup.Value.ToString()), txtSearchInvoiceNumber.Text, chkSearchLike.Checked, txtSearchFolio.Text, Convert.ToInt32(txtYear.Value));

    await Task.WhenAll(new Task[] {t1, t2, t3});
    _invoiceTypes = t1.Result;
    _invoiceAccounts = t2.Result;
    ds = t3.Result;

    if (_ds != null)
    {
        SetupInvoiceGrid();
    }

    confWcf.Dispose();
    seekWcf.Dispose();
}
private async void ProcessSearchAsync()
{

    Data.ConfigurationWCF confWcf = new Data.ConfigurationWCF();
    Task</*typeof _invoiceTypes*/> t1;
    Task</*typeof _invoiceAccounts*/> t2;

    // get catalogs on first search
    if (_invoiceTypes == null && _invoiceAccounts == null)
    {
        t1 = confWcf.GetInvoiceTypesAsync(MainForm.State.Entity);
        t2 = confWcf.GetInvoiceAccountsAsync(MainForm.State.Entity);
    }

    DataSeekWCF seekWcf = new DataSeekWCF();
    Task</*typeof _ds*/> t3 = seekWcf.SearchInvoiceAdminAsync(new Guid(cboEmployer.Value.ToString()), new Guid(cboGroup.Value.ToString()), txtSearchInvoiceNumber.Text, chkSearchLike.Checked, txtSearchFolio.Text, Convert.ToInt32(txtYear.Value));

    _invoiceTypes = await t1;
    _invoiceAccounts = await t2;
    _ds = await t3;

    if (_ds != null)
    {
        SetupInvoiceGrid();
    }

    confWcf.Dispose();
    seekWcf.Dispose();
}