我有一个事件处理程序附加到DataGridView上的selectionChanged事件。在这个处理程序中,我需要创建并加载图像,然后将其显示在图片框中。我遇到的麻烦是,如果我在行选择之间快速跳转应用程序似乎挂起,这是我试图避免的问题。
这是我的代码:
private void loadJobSheet(Job currentJob)
{
if (this.jobCardImageThread != null && this.jobCardImageThread.IsAlive)
this.jobCardImageThread.Abort();
Image jobCardImage = null;
this.jobCardImageThread = new Thread(new ThreadStart(
delegate()
{
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
}
));
jobCardImageThread.Start();
this.picLoadingJobCard.Visible = true;
jobCardImageThread.Join();
if (jobCardImage != null)
{
this.picJobCard.Image = jobCardImage;
this.picLoadingJobCard.Visible = false;
}
}
答案 0 :(得分:0)
您正在等待单独的线程完成
jobCardImageThread.Join();
这会阻止暂停应用程序的UI线程。
你应该删除Join()调用,在Join()调用之后从任何东西创建一个单独的方法,并从委托中调用该方法。可能使用Invoke(...)调用切换回UI线程。
答案 1 :(得分:0)
我认为你的问题是jobCardImageThread.Join();使用此语句,您可以告诉Thread等待另一个完成。这样你的用户界面就会挂起。
为什么不使用后台工作者。例如:
将它放入构造函数
this.backgroundWorker = new BackgroundWorker();
this.backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
this.backgroundWorker.WorkerSupportsCancellation = true;
并添加以下方法:
private BackgroundWorker backgroundWorker;
private AutoResetEvent resetEvent = new AutoResetEvent(false);
private Thread thread;
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
this.picLoadingJobCard.Visible = true;
Job currentJob = (Job)e.Argument;
SavedDocument document = currentJob.SavedDocument;
DocumentConverter<Bitmap> converter = DocumentConverterFactory<Bitmap>.getDocumentConverterForType(Path.GetExtension(document.Document_Name).Replace('.', ' ').Trim().ToUpper(), typeof(Bitmap));
Image jobCardImage = (Image)converter.convertDocument(FileUtils.createTempFile(document.Document_DocumentData.ToArray(), document.Document_Name));
e.Result = jobCardImage;
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//error-handling
}
else if (e.Cancelled)
{
//cancel-handling
}
else
{
Image jobCardImage = e.Result as Image;
if (jobCardImage != null)
this.picJobCard.Image = jobCardImage;
}
this.picLoadingJobCard.Visible = false;
this.resetEvent.Set();
}
private void loadJobSheet(Job currentJob)
{
if (this.thread != null)
this.thread.Abort();
this.thread = new Thread(new ThreadStart(
delegate()
{
if (this.backgroundWorker.IsBusy)
{
this.backgroundWorker.CancelAsync();
this.resetEvent.WaitOne();
}
this.backgroundWorker.RunWorkerAsync(currentJob);
}));
this.thread.Start();
}
答案 2 :(得分:0)
如果你创建一个后台Thread
并在运行后立即调用Join
,你基本上只是浪费时间和内存来创建一个同步方法,因为你的当前线程会阻塞直到后台线程完成。如果当前线程是一个UI线程,这将非常明显。
Also, using Thread.Abort
to kill a thread is not recommended.
我建议创建一个长期存在的后台线程,它大部分时间都会等待来自主线程的signal。这将确保您不会不必要地创建多个线程,以防您收到的请求多于您的工作方法可以处理的请求。
这是一般的想法:
// have a long lived and prosperous thread which handles jobs
private readonly Thread _backgroundWorker;
// you need a way to signal the thread to continue running
private readonly AutoResetEvent _signalNewTask;
// you need a flag indicating you want to stop (to avoid aborting the thread)
private volatile bool _keepRunning;
// and you need to pass the current job to that thread
private volatile Job _currentJob;
循环应该如下所示:
// this runs on a background thread
private void WorkerLoop()
{
Job lastJob = null; Image lastResult = null;
while (_keepRunning)
{
// use an AutoResetEvent for cross-thread signalization
_signalNewTask.WaitOne();
// make sure the app isn't ending
if (!_keepRunning)
break;
// don't bother if we already processed this job
if (lastJob == _currentJob)
continue;
// capture the job in a local variable
lastJob = _currentJob;
// long processing
lastResult = LoadImage(lastJob);
// check if this is still the last requested job
if (_keepRunning && lastJob == _currentJob)
DisplayImage(lastResult);
}
}
要安排作业执行,只需设置字段并发出事件信号:
private void ScheduleNewJob(Job nextJob)
{
// don't waste time if not needed
if (nextJob == _currentJob)
return;
_picLoadingJobCard.Visible = true;
_currentJob = nextJob;
_signalNewTask.Set();
}
您还需要向Form
添加初始化和清理代码:
public SomeForm()
{
InitializeComponent();
_keepRunning = true;
_signalNewTask = new AutoResetEvent(false);
_backgroundWorker = new Thread(WorkerLoop);
_backgroundWorker.IsBackground = true;
_backgroundWorker.Priority = ThreadPriority.BelowNormal;
_backgroundWorker.Start();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// set the running flag to false and signal the thread
// to wake it up
_keepRunning = false;
_signalNewTask.Set();
// this will lock very shortly because the background
// thread breaks when the flag is set
_backgroundWorker.Join();
base.OnFormClosed(e);
}
由于DisplayImage
(或其他)将从后台线程调用,您必须通过调用Invoke
在UI线程上安排:
private void DisplayImage(Image result)
{
if (this.InvokeRequired)
{
Invoke(new Action<Image>(DisplayImage), result);
return;
}
_picLoadingJobCard.Visible = false;
_picJobCard.Image = result;
}