没有 ui 冻结的 Task.Factory.StartNew 延迟
Posted
技术标签:
【中文标题】没有 ui 冻结的 Task.Factory.StartNew 延迟【英文标题】:Task.Factory.StartNew delay without ui freez 【发布时间】:2017-03-04 07:45:59 【问题描述】:当我在UpdateGuItemsAsync
中使用Thread.Sleep
时,程序冻结10 秒,因为线程被阻塞。如果我在“UpdateGuItemsAsync”中使用Task.Delay
,代码会立即执行而不会暂停。我希望在不冻结 UI 的情况下在列表更新之前得到延迟。如何在 .net 3.5 中做到这一点?
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(UpdateGuItemsAsync, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
public void UpdateGuItemsAsync()
System.Threading.Thread.Sleep(10000);
for (int i = 0; i < 100; i++)
Gu45Document gu45Document = new Gu45Document();
gu45Document.Form = "EU-45";
Gu45Documents.Add(gu45Document);
【问题讨论】:
如果您的 UI 代码是 Wpf,我可以建议您查看 UI 主线程Dispatcher
。 ***.com/questions/11625208/…
在 UI 线程上调用 Task.Factory.StartNew
和 TaskScheduler.FromCurrentSynchronizationContext();
与直接在 UI 线程上运行代码是一回事。这里不涉及多线程。
【参考方案1】:
编辑
抱歉,我错过了关于 .Net 3.5 导致 Task.Delay 谈话的要点。下面在 .net 3.5 中找到一个示例,该示例在 10 秒后更新表单中的进度条。按下该表单中的按钮时:
using System;
using System.Threading;
using System.Windows.Forms;
public partial class Form1 : Form
public Form1()
InitializeComponent();
delegate void UpdateGuItemsAsyncDelegate();
void UpdateGuItemsAsync()
for (int i = 0; i < 100; i++)
progressBar1.PerformStep();
private void button1_Click(object sender, EventArgs e)
ThreadPool.QueueUserWorkItem(state =>
Thread.Sleep(10000);
if (progressBar1.InvokeRequired)
UpdateGuItemsAsyncDelegate del = new UpdateGuItemsAsyncDelegate(UpdateGuItemsAsync);
this.Invoke(del);
else
UpdateGuItemsAsync();
);
在 WPF/XAML 中,如果窗口中有进度条和按钮,您几乎可以执行相同的操作:
<StackPanel>
<ProgressBar Name="ProgBar" Minimum="0" Maximum="100" Height="20" />
<Button Name="UpdateCmd" Click="UpdateCmd_Click" Content="Update" />
</StackPanel>
在代码中:
private void UpdateCmd_Click(object sender, RoutedEventArgs e)
ThreadPool.QueueUserWorkItem(state =>
Thread.Sleep(10000);
for (int i = 0; i < 100; i++)
Dispatcher.Invoke(new Action(() =>
ProgBar.Value++;
));
Thread.Sleep(50);
);
【讨论】:
你是对的,它给出了我想要的结果,但在 .net 4.5 中。我需要 .net 3.5 的解决方案。【参考方案2】:在您的情况下,我会使用System.Windows.Threading.DispatcherTimer
并让它在 10 秒后运行一个事件。这适用于 WPF 和 WinForms,但是如果您的项目是 winforms,则需要在项目引用中添加对 WindowsBase
的引用。
private void UpdateGuItemsAsync()
//This line must be called on the UI thread
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += OnUpdateGuiItemsTimerTick;
timer.Start();
private void OnUpdateGuiItemsTimerTick(object sender, EventArgs e)
//Stop the timer.
var timer = sender as DispatcherTimer;
timer.Stop();
//Run the code you where waiting for.
for (int i = 0; i < 100; i++)
Gu45Document gu45Document = new Gu45Document();
gu45Document.Form = "EU-45";
Gu45Documents.Add(gu45Document);
以下是您在 .NET 3.5 中运行并运行的独立程序,以查看它是否有效。您需要做的就是创建一个新的 Windows 窗体项目,添加对 WindowsBase
的引用并使用以下代码
using System;
using System.Windows.Forms;
using System.Windows.Threading;
namespace WindowsFormsApplication1
public partial class Form1 : Form
public Form1()
InitializeComponent();
this.Load += Form1_Load;
private void Form1_Load(object sender, EventArgs e)
UpdateGuiItems();
private void UpdateGuiItems()
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(5);
timer.Tick += OnUpdateGuiItemsTimerTick;
timer.Start();
private void OnUpdateGuiItemsTimerTick(object sender, EventArgs e)
var timer = sender as DispatcherTimer;
timer.Stop();
MessageBox.Show("Am I on the UI thread: " + !this.InvokeRequired);
【讨论】:
【参考方案3】:这应该可以完成工作
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew( () => Thread.Sleep( 10000 ) )
.ContinueWith(
t => UpdateGuItemsAsync(),
CancellationToken.None,
TaskContinuationOptions.None,
uiScheduler );
public void UpdateGuItemsAsync()
// System.Threading.Thread.Sleep(10000);
for (int i = 0; i < 100; i++)
Gu45Document gu45Document = new Gu45Document();
gu45Document.Form = "EU-45";
Gu45Documents.Add(gu45Document);
我正在使用 NuGet 包Task Parallel Library for .NET 3.5。
【讨论】:
Task.Delay
在 .NET 3.5 中不存在(实际上整个 System.Threading.Tasks
命名空间在 3.5 中不存在)
好吧,OP 自己提到他用Task.Delay
测试它 - 我认为他已经包含了 TPL NuGet 包
我不知道是否存在任何 NuGet 包。但是,我不相信这里是这种情况。我认为 OP 在 .NET 4.0 或 4.5 中进行了测试,但想知道如何解决 3.5 中的问题
好的,我编辑了我的答案 - 现在使用 .net 3.5 进行测试:o)【参考方案4】:
您可以使用System.Windows.Forms.Timer,它在等待 10 秒时不会阻塞 UI:
public void UpdateGuItemsAsync()
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 10000;
timer.Tick += (sender, args) =>
timer.Stop();
for (int i = 0; i < 100; i++)
Gu45Document gu45Document = new Gu45Document();
gu45Document.Form = "EU-45";
Gu45Documents.Add(gu45Document);
;
timer.Start();
【讨论】:
我会将timer.Stop();
放在for
循环之前,这样如果您的for
循环处理时间过长,您就不会意外排队第二次触发tick 事件。跨度>
以上是关于没有 ui 冻结的 Task.Factory.StartNew 延迟的主要内容,如果未能解决你的问题,请参考以下文章
使用 FirestoreDecoder 解码 Firestore 文档时 UI 冻结
如何在没有冻结UI的情况下使用加载屏幕正确暂停主线程直到满足条件,