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

バックグラウンドを作成し、それを実行した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 に答える
0

あなたの問題は jobCardImageThread. Join(); だと思います。このステートメントを使用して、スレッドが終了するまで待機するようにスレッドに指示します。このように、UI がハングします。

バックグラウンドワーカーを使用しないでください。例えば:

これをコンストラクタに入れます

        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

あなたがするとき、あなたは別のスレッドが終了するのを待っています

jobCardImageThread.Join();

これにより、UI スレッドがブロックされ、アプリケーションが中断されます。

Join() 呼び出しを削除し、Join() 呼び出しの後に別のメソッドを作成し、デリゲートからそのメソッドを呼び出す必要があります。おそらく Invoke(...) 呼び出しを使用して UI スレッドに戻ります。

于 2013-09-17T09:33:36.783 に答える