1

我有一个从目录中读取每个文件并将其上传到数据库的函数。我无法弄清楚如何在任务返回到 foreach 循环之前等待任务完成,因为它似乎马上就完成了,因为任务需要几秒钟:

foreach (string file in Directory.EnumerateFiles(folderPath, "*.xml"))
{
    //load file
    currentReader = new XmlDataReader(transferInstructions, file);

    currentReader.RowsUploaded += new EventHandler<RowsUploadedEventArgs>(currentReader_RowsUploaded);
    currentReader.TableUploaded += new EventHandler<TableUploadedEventArgs>(currentReader_TableUploaded);
    currentTask = new Task(() => currentReader.executeBulkCopy(initialConnString, workingDatabase));
    currentTask.ContinueWith(task =>
    {
        cleanUp(task);
        //MessageBox.Show("Complete!");
    });
    currentTask.Start();
    writeResult("Started the transfer process.");
    cmdDataTransfer.Text = "CANCEL TRANSFER";
    cmdDataTransfer.ForeColor = Color.DarkRed;
    transferAction = () => cancelCurrentReader();   
}

在继续 foreach 循环之前,我需要等待 MessageBox.show 的位置。到达 cleanUp(task) 需要几秒钟;//MessageBox.Show("完成!"); 部分。

谢谢。

4

1 回答 1

4

C# 5 / .NET 4.5 中的 async-awqait 模式与此完美匹配。我看到您已将其标记为 .NET4,但如果您可以使用Async Targeting Pack,则有一种非常优雅的方法:

void Main()
{
    foreach (string file in Directory.EnumerateFiles(folderPath, "*.xml"))
    {
        currentReader = new XmlDataReader(transferInstructions, file); //load file
        currentReader.RowsUploaded += new EventHandler<RowsUploadedEventArgs>(currentReader_RowsUploaded);
        currentReader.TableUploaded += new EventHandler<TableUploadedEventArgs>(currentReader_TableUploaded);

        var task = Task.Factory.StartNew(() => currentReader.executeBulkCopy(initialConnString, workingDatabase));
        await task;

        cleanUp(task);
        MessageBox.Show("Complete!");
        writeResult("Started the transfer process.");
        cmdDataTransfer.Text = "CANCEL TRANSFER";
        cmdDataTransfer.ForeColor = Color.DarkRed;
        transferAction = () => cancelCurrentReader();   
    }
}

如果您必须保留它 VS2010,则必须模仿 async-await 的功能,类似于:

void MyForm()
{
    _syncContext = SynchronizationContext.Current;
    Execute(Directory.EnumerateFiles(folderPath, "*.xml").GetEnumerator());
}

void Execute(IEnumerator<string> files)
{
    if (!files.MoveNext())
    {
        files.Dispose();
        return;
    }
    Task.Factory.StartNew(() => Execute(files.Current)).ContinueWith(() => Execute(files));
}

public void Execute(string file)
{
    currentReader = new XmlDataReader(transferInstructions, file); //load file
    currentReader.RowsUploaded += new EventHandler<RowsUploadedEventArgs>(currentReader_RowsUploaded);
    currentReader.TableUploaded += new EventHandler<TableUploadedEventArgs>(currentReader_TableUploaded);
    () => currentReader.executeBulkCopy(initialConnString, workingDatabase);
    cleanUp(task);
    _syncContext.Send(updateGUI); 
    transferAction = () => cancelCurrentReader(); 
}
public void updateGUI()
{
    MessageBox.Show("Complete!");
    writeResult("Started the transfer process.");
    cmdDataTransfer.Text = "CANCEL TRANSFER";
    cmdDataTransfer.ForeColor = Color.DarkRed;
}

编辑现在我想到它还有一种更简单的方法。您可以让整个循环在它自己的任务中运行(委派给同步上下文以进行 GUI 工作)。使用上面代码的约定:

Task.Factory.StartNew(() => 
{
    foreach (var file in Directory.EnumerateFiles(folderPath, "*.xml"))
        Execute(file);
}
于 2012-11-24T22:52:40.340 回答