C# 如何在应用程序等待所有任务完成时单击“取消”按钮
这是我的主窗口C# 如何在应用程序等待所有任务完成时单击“取消”按钮,c#,.net,wpf,c#-4.0,C#,.net,Wpf,C# 4.0,这是我的主窗口 <Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525">
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Loaded="EmployeesGridLoaded">
<Grid.RowDefinitions>
<RowDefinition Height="6*" />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name="gEmployees" HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" AlternatingRowBackground="LightBlue" AlternationCount="2" AutoGenerateColumns="False" Grid.Row="0">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Select" Binding="{Binding Select}" Width="1*" />
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" Width="3*" />
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="3*" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="5" Grid.Row="1" >
<Button Content="Process" Margin="5" Click="Process_Click" />
<Button Content="Cancel" Margin="5" Click="Cancel_Click" />
</StackPanel>
</Grid>
</Window>
这是代码
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private List<Employee> Employees = null;
public MainWindow()
{
InitializeComponent();
}
private void EmployeesGridLoaded(object sender, RoutedEventArgs e)
{
Employees = new List<Employee>()
{
new Employee() { Select = false, LastName = "Silly", FirstName = "Dude" },
new Employee() { Select = false, LastName = "Mean", FirstName = "Person" },
new Employee() { Select = false, LastName = "New", FirstName = "Friend" },
new Employee() { Select = false, LastName = "My", FirstName = "Buddy" },
};
gEmployees.ItemsSource = Employees;
}
private void Process_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Factory.StartNew(() =>
{
string[] tResults = Employees
.Where(x => x.Select == true)
.AsParallel()
.Select(x => this.DoSomethingWithThisEmployeee(x.LastName, x.FirstName).Result)
.ToArray();
});
Task.WaitAll(task);
System.Windows.MessageBox.Show("Done");
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
}
private Task<string> DoSomethingWithThisEmployeee(string lastName, string firstName)
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
Random rand = new Random();
System.Threading.Thread.Sleep(rand.Next(30000, 60000));
tcs.SetResult(lastName + " - " + firstName);
return tcs.Task;
}
}
命名空间WpfApplication2
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
私有列表员工=null;
公共主窗口()
{
初始化组件();
}
private void EmployeesGridLoaded(对象发送方,路由目标)
{
雇员=新名单()
{
新员工(){Select=false,LastName=“傻”,FirstName=“Dude”},
新员工(){Select=false,LastName=“Mean”,FirstName=“Person”},
新员工(){Select=false,LastName=“new”,FirstName=“Friend”},
新员工(){Select=false,LastName=“My”,FirstName=“Buddy”},
};
gEmployees.ItemsSource=员工;
}
私有作废处理\u单击(对象发送方,路由目标)
{
Task Task=Task.Factory.StartNew(()=>
{
字符串[]tResults=Employees
.Where(x=>x.Select==true)
.天冬酰胺()
.Select(x=>this.DoSomethingWithThisEmployeee(x.LastName,x.FirstName).Result)
.ToArray();
});
Task.WaitAll(任务);
System.Windows.MessageBox.Show(“完成”);
}
私有无效取消\单击(对象发送者,路由目标)
{
}
私有任务doSomethingWithThisEmployee(字符串lastName,字符串firstName)
{
TaskCompletionSource tcs=新的TaskCompletionSource();
Random rand=新的Random();
系统线程线程睡眠(rand.Next(3000060000));
tcs.SetResult(lastName+“-”+firstName);
返回tcs.Task;
}
}
}
在代码中,对于每个选定的员工,我必须执行一些长时间运行的操作。一旦为所有选定的员工执行了长时间运行的操作,应用程序将执行其他操作。但是,当这些长时间运行的操作正在运行时,我希望“取消”按钮可用,以便用户可以取消操作。问题是Task.WaitAll会阻塞UI,因此无法单击“取消”按钮。有更好的方法吗?有几件事你做错了:
异步等待
Task.Result
、Task.Wait
或Task.WaitAll
将使UI死锁async await
如果您在UI和非UI中完全分离逻辑,那么将更加容易:
private async void Process_Click(object sender, RoutedEventArgs e)
{
var results = await ProcessAsync();
System.Windows.MessageBox.Show("Done");
}
异步方法提供异步执行逻辑,在大多数情况下,甚至不需要任务。运行
如果我理解了你的例子,那么你是在尝试将所做的事情与该员工的所做的事情进行对比。你不应该把AsParallel
和Task
s混在一起。您的处理是CPU受限的(如果处理每个项目不依赖于处理另一个项目,则应使用AaParallel
)或阻塞(为此,您应使用Task
s)
假设您的处理代码是阻塞的,您可以这样编写:
private async Task<IEnumerable<string>> ProcessAsync()
{
var tasks =
from e in this.Employees
where e.Select
select this.DoSomethingWithThisEmployeee(e.LastName, e.FirstName);
return await Task.WhenAll(runningTasks);
}
您的DoSomething使用此Employeee
方法也有问题。它总是在“处理”数据后返回已完成的任务
我相信你想要的是更多的东西:
private Task<string> DoSomethingWithThisEmployeee(string lastName, string firstName, CancellationToken ct)
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
Random rand = new Random();
System.Threading.Timer t;
t = new System.Threading.Timer(
() =>
{
try
{
t.Dispose();
tcs.ThrowIfCancellationRequested();
tcs.TrySetResult(lastName + " - " + firstName);
}
catch OperationCanceledException ex
{
}
catch Exception ex
{
tcs.TrySetException(ex);
}
},
rand.Next(30000, 60000),
Timeout.Infinite);
return tcs.Task;
}
private Task DoSomethingWithThisEmployeee(字符串lastName、字符串firstName、CancellationToken ct)
{
TaskCompletionSource tcs=新的TaskCompletionSource();
Random rand=新的Random();
System.Threading.Timer t;
t=新的System.Threading.Timer(
() =>
{
尝试
{
t、 处置();
tcs.ThrowIfCancellationRequested();
tcs.TrySetResult(lastName+“-”+firstName);
}
捕获操作取消异常
{
}
捕获异常
{
tSystexception(ex);
}
},
下一个兰特(3000060000),
超时(无限);
返回tcs.Task;
}
您可能还需要使用Task.ConfigureAwait(false)
,但我将留给您
阅读上的文章,了解有关async wait
的基本知识(并非如此)
注意:有些代码可能无法编译,需要稍作调整。首先,您不能对单个任务执行WaitAll
。无论如何,您应该使用continuation。或者移动到.NET4.5并使用异步等待。@Hamlet:我不能移动到.NET4.5。所以继续不会导致阻塞?任务完成后将触发继续。使用cancellationtokensource将cancellationtoken传递给每个任务。您需要在每个任务的代码中处理取消,但这允许您启动任务并将控制权返回到UI。然后,cancel按钮将取消cancellationtokensource。这些都是从.Net 4开始的感谢您的详细回复,但是,我只能使用.Net 4.0.OK!我错过了C#-4.0
标签。但是您确实用asyncwait
标记了这个问题,它是C#5.0。你不能用吗?那是我的错。我正在工作的组织有一些奇怪的过程,在引入对较新库的依赖时必须经历这些过程:(如果可能的话,我宁愿不经历痛苦的过程。您仍然可以使用CancellationToken
s和线程,而不是任务。
private Task<string> DoSomethingWithThisEmployeee(string lastName, string firstName, CancellationToken ct)
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
Random rand = new Random();
System.Threading.Timer t;
t = new System.Threading.Timer(
() =>
{
try
{
t.Dispose();
tcs.ThrowIfCancellationRequested();
tcs.TrySetResult(lastName + " - " + firstName);
}
catch OperationCanceledException ex
{
}
catch Exception ex
{
tcs.TrySetException(ex);
}
},
rand.Next(30000, 60000),
Timeout.Infinite);
return tcs.Task;
}