我想在WPF中實現並行任務,它似乎不工作。我有一個非常簡單的應用程序,它有一個按鈕,一個標籤,它執行以下操作:在WPF中的並行任務
- 用戶單擊按鈕。
- 標籤已更新以顯示應用程序正在運行。
- 兩個工人方法並行運行。每個工作者方法都會訪問一個UI元素作爲其算法的一部分。
- 當兩個工作者方法完成時,標籤會更新以顯示每次運行所花費的時間以及應用程序運行的總時間。
當我運行程序時,我得到如下結果:
Worker 1 Time: 00:00:00.2370431
Worker 2 Time: 00:00:00.5505538
Total Time: 00:00:00.8334426
這使我相信,工人方法可能會平行運行,但事情是阻止以同步的方式應用。我認爲如果真正並行運行,總時間應該接近最長的工作人員方法時間。我碰到的一個問題是,因爲我正在訪問工作方法中的UI元素,所以我需要使用Dispatcher.Invoke()
方法。
注意:我閱讀了關於Task-based Asynchronous Programming的文檔。它指出,「爲了更好地控制任務執行或從任務返回值,必須更明確地使用Task對象。」這就是爲什麼我隱式地運行任務;我沒有從工人方法中返回任何東西,我也不需要更多的控制(至少我認爲我不需要)。
我是否正確地假設應用程序正在並行運行worker方法,並且某些內容正迫使應用程序以同步方式運行?爲什麼我會在總時間內獲得工人方法時間的總和?
XAML
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Height="150"
Width="300">
<StackPanel>
<Button x:Name="Button1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Click Me"
Click="Button1_Click" />
<Label x:Name="Label1"
Content=" " />
</StackPanel>
</Window>
C#
namespace WpfApp1
{
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
public partial class MainWindow
{
private string _t1;
private string _t2;
private Stopwatch _sw;
public MainWindow()
{
InitializeComponent();
}
private async void Button1_Click(object sender, RoutedEventArgs e)
{
_sw = new Stopwatch();
_sw.Start();
Label1.Content = "Working...";
await Task.Run(() =>
{
Parallel.Invoke(Work1, Work2);
Dispatcher.Invoke(UpdateLabel);
});
}
private void UpdateLabel()
{
_sw.Stop();
Label1.Content = $"T1: {_t1}\nT2: {_t2}\nTotal: {_sw.Elapsed}";
}
private void Work1()
{
Dispatcher.Invoke(() =>
{
var text = Button1.Content;
var stopWatch = new Stopwatch();
stopWatch.Start();
for (var i = 0; i < 100000000; i++) { }
_t1 = stopWatch.Elapsed.ToString();
}, DispatcherPriority.ContextIdle);
}
private void Work2()
{
Dispatcher.Invoke(() =>
{
var text = Button1.Content;
var stopWatch = new Stopwatch();
stopWatch.Start();
for (var i = 0; i < 250000000; i++) { }
_t2 = stopWatch.Elapsed.ToString();
}, DispatcherPriority.ContextIdle);
}
}
}
您要安排線程池線程擁有它安排兩個線程池線程都要求UI線程完成一大堆工作,讓線程池線程等待完成,然後讓外部線程池線程等待*完成。不要這樣做。如果你想在UI線程中運行兩件事,只需在UI線程中運行它們,不要安排3個線程池線程在那裏做任何事情,而在UI線程中做2件事情。 – Servy
如果我明白你在說什麼,我應該將UI工作放在Dispatcher.Invoke()方法中,並將定時器邏輯放在Dispatcher.Invoke()調用之外? – Halcyon
你不應該在後臺工作中觸摸UI。如果有的話,你應該很少使用'Dispatcher.Invoke'。從UI中獲取所需的任何數據,然後創建一些工作人員併爲其提供數據,然後返回可能計算的結果,然後在後臺工作完成後更新UI,並顯示結果。 – Servy