c#创建图像的线程被取消

时间:2013-09-17 08:59:41

标签: c# .net multithreading visual-studio-2010 datagridview

我有一个事件处理程序附加到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;
        }
    }

3 个答案:

答案 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;
}