C# .NET线程&;锁具及;等待
我试图使我的GUI线程在长时间运行的操作中保持响应。这些操作必须是同步的,因为它们通常是在请求的操作完成之前需要完成的操作 我试图用一个后台工作程序、监视器和一个锁对象来实现这一点。基本上,我希望在长时间运行的进程启动之前启动一个计时器,在后台线程中启动长时间运行的进程,并在后台工作线程上等待,以表明它已完成,然后继续使用依赖代码。如果长时间运行的过程花费的时间太长,则向用户显示“加载…”对话框,以便他们知道应用程序没有崩溃 这方面的一个例子可能是用户单击图形包中的按钮,必须先从磁盘加载一个大图像,然后才能绘制图像,然后将pi打印到小数点后100万位 我无法使从磁盘加载映像异步,这会使UI保持响应,因为用户可能会启动另一个会扰乱程序状态的操作(即撤消操作) 我可以简单地将光标更改为沙漏并使用它,但在许多情况下,我希望用户也能够取消操作-带有取消按钮的“加载…”对话框可以很好地解决这一问题C# .NET线程&;锁具及;等待,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,我试图使我的GUI线程在长时间运行的操作中保持响应。这些操作必须是同步的,因为它们通常是在请求的操作完成之前需要完成的操作 我试图用一个后台工作程序、监视器和一个锁对象来实现这一点。基本上,我希望在长时间运行的进程启动之前启动一个计时器,在后台线程中启动长时间运行的进程,并在后台工作线程上等待,以表明它已完成,然后继续使用依赖代码。如果长时间运行的过程花费的时间太长,则向用户显示“加载…”对话框,以便他们知道应用程序没有崩溃 这方面的一个例子可能是用户单击图形包中的按钮,必须先从磁盘加载一个大图
我最初的目标是使用lock对象和
System.Threading.Monitor.Enter()
,以便UI线程等待长时间运行的线程完成,然后继续执行。如果计时器在长时间运行的线程完成之前触发,那么UI线程仍然可以处理事件并在屏幕上绘制对话框
我遇到的问题是,在UI线程尝试获得锁定之前,我无法让后台工作程序锁定对象
令人恼火的是,我正在使用一些第三方代码,这是一个非常黑的盒子来进行处理。因此,我不能将代码裁剪为线程友好型,并报告其进度或支持取消
我的问题 是否有任何经过验证的方法包装第三方代码,以便UI线程保持响应,并在需要时显示“取消”对话框在许多情况下,长时间运行的操作几乎立即完成,并且不需要显示对话框
进一步澄清一下 我为什么要这样做?异步操作是windows应用程序的宠儿 我不想在启动长时间运行的异步操作时锁定用户界面的每个方面,然后在完成后解锁每个方面。我可以-通过设置光标或物理禁用所有对接等,但实际上我更希望能够简单地将调用包装在“某个对象/方法等”中,这将允许在(且仅当)操作花费足够长的时间影响用户时弹出对话框。我不必担心对执行流的更改,我仍然(总体上)能够在代码中维护原子操作(而不是在回调之间拆分),并且仍然有一个“响应”的UI
我可以理解为什么我至今没有成功地将后台工作人员/线程编译成同步阻塞线程,但是我担心在GUI线程中,我将不得不使用<(代码)>(真){睡眠()} /代码>路由,而不是使用锁。
在您进一步了解之前,我将认真考虑.NET中的类。你说你用了一个“背景工作者”,所以我不确定你是不是这个意思。它具有从worker函数中调用进度通知到UI的功能。有了进度通知,应该可以大大减少您对同步对象的需求。
我会将您的worker放在一个单独的类中,在它自己的线程上运行它,并使用回调来通知您的UI进度和完成情况。当工作线程完成时,UI可以使用回调更新进度条并启用其他控件。回调还可以返回值,以便您可以使用它完全停止工作进程 下面是一个非常简单的示例:public delegate void CallbackDelegate(string messageArg);
class Program
{
static void Main(string[] args)
{
Worker worker = new Worker();
worker.Callback = new CallbackDelegate(WorkerStatus);
Thread thread = new Thread(new ThreadStart(worker.DoSomething));
thread.IsBackground = true;
thread.Start();
Console.ReadLine(); // wait for enter key
}
static void WorkerStatus(string statusArg)
{
Console.WriteLine(statusArg);
}
}
public class Worker
{
public CallbackDelegate Callback { private get; set; }
public void DoSomething()
{
for (int i = 0; i < 10; i++)
{
Callback(i.ToString());
}
Callback("done");
}
}
public委托void CallbackDelegate(字符串messageArg);
班级计划
{
静态void Main(字符串[]参数)
{
工人=新工人();
worker.Callback=newcallbackdelegate(WorkerStatus);
线程=新线程(新线程开始(worker.DoSomething));
thread.IsBackground=true;
thread.Start();
Console.ReadLine();//等待回车键
}
静态无效WorkerStatus(字符串状态arg)
{
控制台写入线(statusArg);
}
}
公社工人
{
公共回调委托回调{private get;set;}
公共无效剂量测定法()
{
对于(int i=0;i<10;i++)
{
回调(i.ToString());
}
收回(“完成”);
}
}
我假设这是windows窗体?
在实际阻塞时恢复响应的一种便宜方法是这样一种模式:
// Method call would be foo.DoWork()
Action f = foo.DoWork;
var asyncresult = f.BeginInvoke(null, null);
while (!asyncresult.IsCompleted)
Application.DoEvents();
在这种情况下,用户仍然可以单击按钮取消操作。请注意,中止线程通常不是一个好主意-它会破坏程序可能正在运行的任何状态。更好的办法是定期检查某个bool cancel字段是否已设置为true,然后在下一个可能的时刻优雅地结束操作。我就是这样做的: 启动长时间操作的代码:
private void button1_Click(object sender, EventArgs e)
{
Thread thr = new Thread(LongMethod);
thr.Start();
// wait for 250 ms. If the thread is not finished, we show a from in a modal way
// saying something like "please wait for the operation to complete"
if(!thr.Join(250))
{
pleaseWaitForm.Thread = thr;
pleaseWaitForm.ShowDialog();
}
}
然后,在“请稍候”表格中
最后,在长方法中:
private void LongMethod(){
// Do some stuff
Threading.Thread.Sleep(100000);
// Actually close the "please wait" window
pleaseWaitForm.Invoke(new Action(()=>pleaseWaitForm.JoinAndClose()))
}
这有点粗略,可能有一些bug,但总体思路很简单-执行一个定时的Join()
仅在操作很长时显示对话框,然后从长时间运行的线程本身关闭对话框。为什么不能进行加载
private void LongMethod(){
// Do some stuff
Threading.Thread.Sleep(100000);
// Actually close the "please wait" window
pleaseWaitForm.Invoke(new Action(()=>pleaseWaitForm.JoinAndClose()))
}