【问题标题】:Task.Run for uploading and update progress return error [duplicate]Task.Run 用于上传和更新进度返回错误 [重复]
【发布时间】:2013-02-26 02:24:07
【问题描述】:

我有以下代码将文件上传到服务器并在栏中更新上传进度。

private void UploadButton_Click(object sender, EventArgs e)
{
    Cursor = Cursors.WaitCursor;
    try
    {
        // get some info about the input file
        System.IO.FileInfo fileInfo = new System.IO.FileInfo(FileTextBox.Text);
        UploadDocument(fileInfo);
        // show start message
        LogText("Starting uploading " + fileInfo.Name);
        LogText("Size : " + fileInfo.Length);
    }
    catch (Exception ex)
    {
        LogText("Exception : " + ex.Message);
        if (ex.InnerException != null) LogText("Inner Exception : " + ex.InnerException.Message);
    }
    finally
    {
        Cursor = Cursors.Default;
    }
}

private async void UploadDocument(System.IO.FileInfo fileInfo)
{
    var someTask = await Task.Run<bool>(() =>
    {
        // open input stream
        using (System.IO.FileStream stream = new System.IO.FileStream(FileTextBox.Text, System.IO.FileMode.Open, System.IO.FileAccess.Read))
        {
            using (StreamWithProgress uploadStreamWithProgress = new StreamWithProgress(stream))
            {
                uploadStreamWithProgress.ProgressChanged += uploadStreamWithProgress_ProgressChanged;

                // start service client
                FileTransferWCF.FileTransferServiceClient client = new FileTransferWCF.FileTransferServiceClient();
                //FileTransferClient.FileTransferServiceClient client = new FileTransferClient.FileTransferServiceClient();

                // upload file
                client.UploadFile(fileInfo.Name, fileInfo.Length, uploadStreamWithProgress);

                LogText("Done!");

                // close service client
                client.Close();
            }
        }

        return true;
    });
}

void uploadStreamWithProgress_ProgressChanged(object sender, StreamWithProgress.ProgressChangedEventArgs e)
{
    if (e.Length != 0)
        progressBar1.Value = (int)(e.BytesRead * 100 / e.Length);
}

我收到错误消息:“跨线程操作无效:控件'progressBar1'从创建它的线程以外的线程访问。”在行中:

progressBar1.Value = (int)(e.BytesRead * 100 / e.Length);

也许我做错了。我是 .Net 任务库的新手。

有什么线索吗?

【问题讨论】:

    标签: c# winforms task-parallel-library async-await


    【解决方案1】:

    我建议阅读我的async/await introasync 编程的准则之一是避免async void;也就是说,使用async Task 而不是async void,除非您正在编写事件处理程序。

    此外,一旦您开始使用async,请尝试在任何地方使用它。它确实简化了代码。

    因此,您的代码可以这样更改(假设 StreamWithProgress 使用 EAP 约定):

    private async void UploadButton_Click(object sender, EventArgs e)
    {
      UploadButton.Enabled = false;
      Cursor = Cursors.WaitCursor;
      try
      {
        // get some info about the input file
        System.IO.FileInfo fileInfo = new System.IO.FileInfo(FileTextBox.Text);
        var task = UploadDocument(fileInfo);
    
        // show start message
        LogText("Starting uploading " + fileInfo.Name);
        LogText("Size : " + fileInfo.Length);
    
        await task;
    
        LogText("Done!");
      }
      catch (Exception ex)
      {
        LogText("Exception : " + ex.Message);
        if (ex.InnerException != null) LogText("Inner Exception : " + ex.InnerException.Message);
      }
      finally
      {
        Cursor = Cursors.Default;
        UploadButton.Enabled = true;
      }
    }
    
    private async Task UploadDocument(System.IO.FileInfo fileInfo)
    {
      // open input stream
      using (System.IO.FileStream stream = new System.IO.FileStream(FileTextBox.Text, System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.Read, 4096, true))
      { 
        using (StreamWithProgress uploadStreamWithProgress = new StreamWithProgress(stream))
        {
          uploadStreamWithProgress.ProgressChanged += uploadStreamWithProgress_ProgressChanged;
    
          // start service client
          FileTransferWCF.FileTransferServiceClient client = new FileTransferWCF.FileTransferServiceClient();
    
          // upload file
          await client.UploadFileAsync(fileInfo.Name, fileInfo.Length, uploadStreamWithProgress);
    
          // close service client
          client.Close();
        }
      }
    }
    
    void uploadStreamWithProgress_ProgressChanged(object sender, StreamWithProgress.ProgressChangedEventArgs e)
    {
      if (e.Length != 0)
        progressBar1.Value = (int)(e.BytesRead * 100 / e.Length);
    }
    

    【讨论】:

    • 您不想在 finally 运行之前的某个时间点 awaitUploadDocument 执行任务吗?
    • 我什至认为,考虑到重构,LogText("Done!"); 确实属于UploadButton_Click,从而使UploadDocument 能够独立于 UI(让文本框文本成为另一个参数)这将允许它完全移出 UI 类,并进入某种工作类。
    • 另一个好点。 :)
    【解决方案2】:

    您需要在 UI 线程上进行 UI 更新。

    progressBar1.Invoke(new Action(() => 
        { progressBar1.Value = (int)(e.BytesRead * 100 / e.Length); }));
    

    或者(如果您不想在方法返回之前阻塞)

    progressBar1.BeginInvoke(new Action(() => 
        { progressBar1.Value = (int)(e.BytesRead * 100 / e.Length); }));
    

    抱歉,我暂时无法访问 VS。

    【讨论】:

    • 更好的是,您可以使用BeginInvoke,因为您无需等待调用结果。
    • @MarekDzikiewicz 所以它会像:progressBar1.BeingInvoke(new Action(() => { progressBar1.Value = (int)(e.BytesRead * 100 / e.Length); })) ; ??
    • 是的,该方法具有相同的签名,它只是在委托像Invoke 那样执行之前不会阻塞。
    猜你喜欢
    • 2015-04-10
    • 2017-08-23
    • 2018-10-21
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 2016-11-11
    • 2014-11-13
    相关资源
    最近更新 更多