0

这是我在这个论坛上的第一个问题,由于我不是以英语为母语的人,我希望你对我放轻松,以防我做错或说错了什么。

所以,这是我的问题:

我喜欢解决一个简单的问题(看起来如此),但我已经研究了一个多星期并尝试了不同的东西,但到目前为止都没有奏效,我总是偶然发现同样的问题。

我喜欢做的是打开一个进度表,然后开始两个长时间运行的任务(例如从两个数据库中获取数据)。这些长时间运行的任务向进度表报告他们的进度,完成后进度表关闭并打开另一个表单以显示两个长时间运行的任务的结果。在进度表关闭和程序继续(或关闭)之前,这两项任务都必须完成(或取消/失败)。

因此,进度表(Form1)只包含两个列表框(ListBox1 和 ListBox2)和以下代码:

Public Delegate Sub ShowProgressDelegate(ByVal message As String)

Public Class Form1

Public Sub AddMessage1(ByVal message As String)
  If String.IsNullOrEmpty(message) Then
    Exit Sub
  End If

  If Me.InvokeRequired Then
    Me.Invoke(New ShowProgressDelegate(AddressOf Me.AddMessage1), New Object() {message})
  Else
    Me.ListBox1.Items.Add(message)
    Me.ListBox1.SelectedIndex = Me.ListBox1.Items.Count - 1
    Application.DoEvents()
  End If
End Sub

Public Sub AddMessage2(ByVal message As String)
  If String.IsNullOrEmpty(message) Then
    Exit Sub
  End If

  If Me.InvokeRequired Then
    Me.Invoke(New ShowProgressDelegate(AddressOf Me.AddMessage2), New Object() {message})
  Else
    Me.ListBox2.Items.Add(message)
    Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
    Application.DoEvents()
  End If
End Sub

End Class

然后我有我的 TestClass 模拟长时间运行的任务并引发进度事件:

Imports System.Threading

Public Class TestClass

  Public Event ShowProgress(ByVal message As String)

  Private _milliSeconds As UShort

  Public Sub New(milliSeconds As UShort)
    _milliSeconds = milliSeconds
  End Sub

  Public Function Run() As UShort
    For i As Integer = 1 To 20
      RaiseEvent ShowProgress("Run " & i)
      Thread.Sleep(_milliSeconds)
    Next i

    Return _milliSeconds
  End Function

End Class

最后我有我的主要程序,它试图把这两个放在一起:

Imports System.Threading.Tasks
Imports System.ComponentModel

Public Class Start
  Private Const MULTI_THREAD As Boolean = True

  Public Shared Sub Main()
    Dim testClass(1) As TestClass
    Dim testTask(1) As Task(Of UShort)
    Dim result(1) As UShort

    testClass(0) = New TestClass(50)
    testClass(1) = New TestClass(100)

    Using frm As Form1 = New Form1
      frm.Show()

      AddHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
      AddHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2

      If MULTI_THREAD Then
        testTask(0) = Task(Of UShort).Factory.StartNew(Function() testClass(0).Run)
        testTask(1) = Task(Of UShort).Factory.StartNew(Function() testClass(1).Run)

        Task.WaitAll(testTask(0), testTask(1))

        result(0) = testTask(0).Result
        result(1) = testTask(1).Result
      Else
        result(0) = testClass(0).Run
        result(1) = testClass(1).Run
      End If

      RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
      RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2

      frm.Close()
    End Using

    MessageBox.Show("Result 1: " & result(0) & "; Result 2: " & result(1))
  End Sub

End Class

如果我将常量 MULTI_THREAD 设置为 false,则一切正常(但按顺序)。但是如果我将 if 设置为 true,它只会显示表单,但不会显示任何进度,也不会显示生成的消息框。如果我调试它永远不会到达 Task.WaitAll(...) 之后的行。

我已经尝试过其他多线程方法,例如使用普通线程(无返回值)、后台工作人员(仅百分比进度,无文本消息)、委托上的 BeginInvoke/EndInvoke,但没有任何效果,或者它显示出与上述相同的行为。

我在另一个论坛上被告知,Task.WaitAll 阻塞了 UI 线程,因为它是在 UI 线程上调用的,但我不敢相信,因为 MSDN 明确指出 Task.WaitAll 等待移交的任务完成或者失败,我没有交出 UI 线程。

所以,我不知道还能尝试什么,所以我希望你能向我指出我的错误,或者告诉我另一种方法来尝试解决我的小问题。

非常感谢您提前。

更新:
我做了更多的测试和调试。如果我将表单代码中的两个“Me.Invoke(...)”更改为“Me.BeginInvoke(...)”,那么我的表单中仍然没有显示任何进度消息,但至少任务有效我得到了我的关闭消息框。因此,进度消息的实际交付或显示似乎会导致问题。

也许这会触发你的想法?

再次感谢你。

更新二:
经过大量的试验和错误,它似乎工作,至少一点点。我在创建任务时交出了“TaskScheduler.FromCurrentSynchronizationContext”,而不是表单代码中的“Me.InvokeRequired”部分。所以我至少显示了我的进度消息,但任务仍然没有完全并行运行。但这是我下一个问题的主题。

4

3 回答 3

0

您的进程可能会占用系统端可用的大量资源。它只是开始新任务,尝试为繁重的进程启动完整的新线程

你甚至可以在单独的线程中启动你的进度 UI,这样它就不会停止响应

Thread nThread;
private void Button1_Click(System.Object sender, System.EventArgs e)
{
    nThread = new Thread(ShowDlg);
    nThread.Start();
}

private void ShowDlg()
{
    Form1 nWin = new Form1();
    nWin.ShowDialog();
}
于 2013-04-09T06:35:47.667 回答
0

您在启动任务后调用 WaitAll,这将阻塞 UI 线程,因此您的进度将不会显示。

您应该指定一个回调或延续任务,以便在任务完成后执行,并在其中做最后的事情,例如显示结果并关闭进度表。

Task.Factory.ContinueWhenAll(tasks, Sub(antecedents)
  result(0) = testTask(0).Result
  result(1) = testTask(1).Result
  RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
  RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2

  frm.Close()
  MessageBox.Show("Result 1: " & result(0) & "; Result 2: " & result(1))
End Sub

我通常不是 vb.net 程序员,因此您可能需要对其进行一些调整以确保闭包正常工作,并将其中一些调用编组回 ui 线程

于 2013-04-09T07:03:46.963 回答
0

尝试将 Task 更改为 BackgroundWorker。这个类是为表单创建的,并且有很好的 api 来显示关于你的工作的表单信息。看这里的教程

更新 完整示例,因为在评论中它看起来很丑:

public partial class Form1 : Form
{
    private BackgroundWorker bw = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();

        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if ((e.Cancelled == true))
        {
            this.tbProgress.Text = "Canceled!";
        }

        else if (!(e.Error == null))
        {
            this.tbProgress.Text = ("Error: " + e.Error.Message);
        }

        else
        {
            this.tbProgress.Text = "Done!";
        }
    }

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.tbProgress.Text = (string)e.UserState;            
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = sender as BackgroundWorker;

        for (int i = 1; (i <= 10); i++)
        {
            if ((worker.CancellationPending == true))
            {
                e.Cancel = true;
                break;
            }
            else
            {
                // Perform a time consuming operation and report progress.
                System.Threading.Thread.Sleep(500);
                worker.ReportProgress((i * 10), "status you want "+i.ToString());
            }
        }
    }
}
于 2013-04-09T06:27:12.950 回答