Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET线程&;锁具及;等待_C#_.net_Multithreading_Locking - Fatal编程技术网

C# .NET线程&;锁具及;等待

C# .NET线程&;锁具及;等待,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,我试图使我的GUI线程在长时间运行的操作中保持响应。这些操作必须是同步的,因为它们通常是在请求的操作完成之前需要完成的操作 我试图用一个后台工作程序、监视器和一个锁对象来实现这一点。基本上,我希望在长时间运行的进程启动之前启动一个计时器,在后台线程中启动长时间运行的进程,并在后台工作线程上等待,以表明它已完成,然后继续使用依赖代码。如果长时间运行的过程花费的时间太长,则向用户显示“加载…”对话框,以便他们知道应用程序没有崩溃 这方面的一个例子可能是用户单击图形包中的按钮,必须先从磁盘加载一个大图

我试图使我的GUI线程在长时间运行的操作中保持响应。这些操作必须是同步的,因为它们通常是在请求的操作完成之前需要完成的操作

我试图用一个后台工作程序、监视器和一个锁对象来实现这一点。基本上,我希望在长时间运行的进程启动之前启动一个计时器,在后台线程中启动长时间运行的进程,并在后台工作线程上等待,以表明它已完成,然后继续使用依赖代码。如果长时间运行的过程花费的时间太长,则向用户显示“加载…”对话框,以便他们知道应用程序没有崩溃

这方面的一个例子可能是用户单击图形包中的按钮,必须先从磁盘加载一个大图像,然后才能绘制图像,然后将pi打印到小数点后100万位

我无法使从磁盘加载映像异步,这会使UI保持响应,因为用户可能会启动另一个会扰乱程序状态的操作(即撤消操作)

我可以简单地将光标更改为沙漏并使用它,但在许多情况下,我希望用户也能够取消操作-带有取消按钮的“加载…”对话框可以很好地解决这一问题


我最初的目标是使用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()))
}