1

我有一个附加到 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;
        }
    }
4

3 回答 3

0

当您这样做时,您正在等待单独的线程完成

jobCardImageThread.Join();

这会阻塞 UI 线程,从而挂起应用程序。

您应该删除 Join() 调用,在 Join() 调用之后创建一个单独的方法,然后从委托中调用该方法。可能使用 Invoke(...) 调用切换回 UI 线程。

于 2013-09-17T09:33:36.783 回答
0

我认为你的问题是 jobCardImageThread.Join(); 使用此语句,您可以告诉您的线程等待另一个完成。这样你的用户界面就会挂起。

你为什么不使用后台工作者。例如:

把它放到你的构造函数中

        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();
    }
于 2013-09-17T09:36:41.180 回答
0

如果你创建一个后台Thread并在运行后立即调用Join,你基本上只是浪费时间和内存来创建一个同步方法,因为你当前的线程会阻塞,直到后台线程完成。如果当前线程是 UI 线程,这将非常明显。

此外,Thread.Abort不建议使用杀死线程。

我建议创建一个长期存在的后台线程,该线程大部分时间都会等待来自主线程的信号。这将确保您不会不必要地创建多个线程,以防您收到的请求超出您的工作方法可以处理的数量。

这是一般的想法:

// 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(或其他)将从后台线程调用,因此您必须通过调用在 UI 线程上进行调度Invoke

private void DisplayImage(Image result)
{
    if (this.InvokeRequired)
    {
        Invoke(new Action<Image>(DisplayImage), result);
        return;
    }

    _picLoadingJobCard.Visible = false;
    _picJobCard.Image = result;
}
于 2013-09-17T11:33:22.863 回答