【问题标题】:Method accessed by more than one thread in C# Using ProgressbarC#中多个线程使用Progressbar访问的方法
【发布时间】:2012-10-10 05:48:04
【问题描述】:

我有多个线程访问一个方法。

该方法会在线程执行时更新表单上的进度条。

该方法会更新我的 windows 窗体上的进度条,以显示线程正在完成处理文件的进度。

您将如何管理该方法以避免死锁。我无法锁定它,不知道是我做错了,还是在这种情况下我应该锁定它。

public void myWorkerClass()
        {
            int amountToScan = 4;

            lblcount.BeginInvoke(
               ((Action)(() => lblcount.Text = "Files Checked " + atPDFNumber.ToString() + " of " + amountToScan.ToString())));

            decimal percentageComplete = ((decimal)atPDFNumber / (decimal)amountToScan) * 100;

            backgroundWorker1.ReportProgress((int)percentageComplete);

        }

发生的情况是,在最后一行,它告诉我“这个操作已经调用了 OperationCompleted 并且进一步的调用是非法的。”

我认为我打开了已经由第一个线程或类似的东西处理的东西。

这是在多线程中使用进度条的好方法(我的想法或原则是否正确),还是我的代码中只是一个小错误?

我想让这个问题对其他人有价值的方法是回答这个问题,如何在多个线程访问的方法中访问全局变量。 因为我认为这就是这个问题的方向。

【问题讨论】:

  • 我认为您需要为进度条对象创建一个委托,就像您有多个线程更新表单中的列表框一样。
  • 您正在对不再执行任何工作的后台工作人员调用 ReportProgress。使用对 backgroundWorker1 的硬引用并且拥有多个线程永远不会正确。
  • 所以你说拥有多个主题不是一个好主意?为什么不?和@Derek,现在正在查找代表

标签: c# multithreading methods progress-bar


【解决方案1】:

看起来您正在使用我不熟悉的 BackgroundWorker 类。

但您还要求使用多线程和 ProgressBar 的“通用”设计。这个例子是我从多个线程更新 ProgressBar 的方式。

首先,您需要一个公共委托(可能在某个辅助类中)

//is called when the worker-progress is changed
public delegate void ProgressChangedHandler(object sender, ProgressEventArgs e);

// some own EventArgs
public class ProgressEventArgs : EventArgs
{
    public int Percentage { get; private set; }

    public string Message { get; private set; }

    public ProgressEventArgs(int percentage, string message)
    {
        Percentage = percentage;
        Message = message;
    }
}

在每个工人阶级中你必须注册代表

// event for reporting progress
private event ProgressChangedHandler ProgressChanged;

// register Eventhandler via dependency injection or own method
public void RegisterDelegate(ProgressChangedHandler progressChangedHandler)
{
    ProgressChanged += progressChangedHandler;
}

在包含 ProgressBar 的 UI 类中,您需要一个更新进度的方法。但是对该方法的每次调用都在调用者的线程中执行(这对于非线程安全的 UI 可能会出现问题)。

为避免这种情况,您可以调用调度程序,以便在您的 UI 线程而不是工作线程中处理每个引发的事件。

private void StatusUpdate(object sender, ProgressEventArgs args)
{    
    if (Dispatcher.Thread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
    {
        // call from a worker thread
        var statusUpdateDelegate = new ProgressChangedHandler(this.StatusUpdate);

        Dispatcher.Invoke(statusUpdateDelegate, DispatcherPriority.Normal, sender, args);
    }
    else
    {
        // direct call from the UI thread
        lblProgress.Content = args.Message;
        pbProgress.Value = args.Percentage;
    }
}

您可以将此方法作为委托传递给工作人员

worker.RegisterEventHandler(StatusUpdate);

最后,您可以通过每个线程的事件调用委托

public void DoVeryHardWork()
{
    // do stuff
    ProgressChanged(this, new ProgressEventArgs(progress, "some message"));
}

【讨论】:

  • 嗯,这无疑澄清了我对线程、委托和助手类的理解。谢谢
  • 那么一般的运行线程也应该以这种方式完成吧?因为此时我只使用锁定某些事物的对象/锁定方法。
  • 你不能这么说。锁定与我在这里展示的不同。您仍然需要锁定来操作值和对象,以便每个线程都可以自行处理。在这种情况下,我们必须为 UI 使用 Dispatcher,通常您不需要它,因此锁定是正确的方法。
  • 哦,好吧,我明白了。谢谢你的帮助
猜你喜欢
  • 2011-12-31
  • 2011-12-18
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 2019-12-03
  • 2011-01-24
  • 2012-08-28
  • 2015-12-31
相关资源
最近更新 更多