在 C# 中没有 GUI 的 DLL 中引发跨线程事件



我写了一个DLL,可以做很多事情。它所做的一件事是在大量传入消息中搜索特定消息,清理消息,并在找到消息时引发事件,并通过 EventArgs 传递数据。

目前 DLL 正在工作,但搜索消息的例程会减慢所有内容并使系统响应缓慢,因为要处理的数据太多。

我想将此搜索移动到它自己的线程,一旦找到消息,它就会在主线程上引发一个事件并将消息数据传递给主线程。我知道如何使线程来完成工作,但我不知道如何使线程在主线程上引发事件并将数据传递给它。有人可以告诉我如何做到这一点,或者指出一个例子吗?

我尝试创建一个委托并触发一个事件,这有效,但是当我尝试传递数据时,我得到了一个跨线程异常。

一些可能很重要的细节。很可能根本没有 GUI,并且可能仍然是控制台应用程序。消息将始终传入,DLL 必须不断搜索它们。DLL 正在查找的消息可能每秒出现 1 次,也可能在消息之间间隔几天。

更新一个真正的简单项目,说明我想做什么。这段代码很粗糙,因为我把它放在一起来测试一下。

如果您遵循代码,您将看到一切运行良好,直到 CollectData 函数中的行:

StatusUpdated(this, null);

被称为。此时,由于 UI 线程和数据收集线程,它会导致跨线程异常。我知道我可以通过在 UI 线程上使用调用来解决此问题,但如上所述,这很可能不会与任何类型的 GUI 一起使用(但它应该能够处理它)。因此,需要在 BuildResultsManager 类中修复跨线程异常,我认为在函数BRMgr_StatusUpdated中。如何在不更改 MainWindow 类中的任何代码的情况下将数据(在本例中为 currentVal 的值)获取到 UI 线程。

可以通过创建包含 2 个项目的解决方案来运行此任务,其中 1 个用于前 2 个文件作为 dll。第二个作为 wpf 项目,引用 dll,并在窗体上放置一个名为 status 的文本框。

主类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace BuildResultsManager
{
/// <summary>
/// Delegate to notify when data has changed
/// </summary>
/// <param name="sender">unused</param>
/// <param name="e">unused</param>
public delegate void StatusUpdatedEventHandler(object sender, EventArgs e);
/// <summary>
/// Connects to the PLC via PVI and reads in all the build results data, conditions it, updates values and reads/store into the database
/// </summary>
public class BuildResultsManager
{
    #region events
    // Change in The Build Results Manger Status
    public event StatusUpdatedEventHandler StatusUpdated;
    #endregion
    #region Local variables
    private Thread collectionThread;
    private string statusMessage;
    DataCollector dataCollection;
    #endregion
    #region Properties
    /// <summary>
    /// Current Status of the Build Results manager
    /// </summary>
    public String StatusMessage
    {
        get
        {
            return statusMessage;
        }
        private set
        {
            statusMessage = value;
            if (StatusUpdated != null)
                StatusUpdated(this, null);
        }
    }
    #endregion
    #region Constructors
    /// <summary>
    /// Default constructor
    /// </summary>
    public BuildResultsManager()
    {
        StatusMessage = "successfully initialized";
        // start the thread that will collect all the data
        dataCollection = new DataCollector();
        dataCollection.StatusUpdated += new DCStatusUpdatedEventHandler(BRMgr_StatusUpdated);
        collectionThread = new Thread(new ThreadStart(dataCollection.CollectData));
        collectionThread.Start();
    }
    /// <summary>
    /// EVent to handle updated status text
    /// </summary>
    /// <param name="sender">unused</param>
    /// <param name="e">unused</param>
    void BRMgr_StatusUpdated(object sender, EventArgs e)
    {
        StatusMessage = dataCollection.currentVal.ToString();
    }
    #endregion
}
}

将执行所有线程工作的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Threading;
using System.Threading;
namespace BuildResultsManager
{
/// <summary>
/// Delegate to notify when data has changed
/// </summary>
/// <param name="sender">unused</param>
/// <param name="e">unused</param>
public delegate void DCStatusUpdatedEventHandler(object sender, EventArgs e);
/// <summary>
/// Handles all of the data collection and conditioning
/// </summary>
class DataCollector
{
    #region events
    // Change in The Build Results Manger Status
    public event DCStatusUpdatedEventHandler StatusUpdated;
    #endregion
    private const long updateInterval = 1000;
    private Stopwatch updateTimer;       
    public int currentVal;
    #region local variables
    private bool shouldStop;
    #endregion
    /// <summary>
    /// Default Constructor
    /// </summary>
    public DataCollector()
    {
        shouldStop = false;
        updateTimer = new Stopwatch();
        updateTimer.Start();            
    }
    /// <summary>
    /// Main task that listens for new data and collects it when it's available
    /// </summary>
    public void CollectData()
    {
        currentVal = 5;
        while (!shouldStop && currentVal < 10)
        {
            if(updateTimer.ElapsedMilliseconds > updateInterval)
            {
                currentVal++;
                if (StatusUpdated != null)
                {
                    StatusUpdated(this, null);
                }
                //reset the timer
                updateTimer.Restart();
            }
        }
    }
    /// <summary>
    /// Asks the thread to stop
    /// </summary>
    public void RequestStop()
    {
        shouldStop = true;
    }
}
}

wpf 项目的代码隐藏:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    #region Local variables
    private string BRMgrStatus;
    private BuildResultsManager.BuildResultsManager BRMgr;
    #endregion
    public MainWindow()
    {
        InitializeComponent();
        // create an instance of the build manager
        BRMgr = new BuildResultsManager.BuildResultsManager();
        if(BRMgr != null)
        {
            status.Text = BRMgr.StatusMessage;
            BRMgr.StatusUpdated += new StatusUpdatedEventHandler(BRMgr_StatusUpdated);
        }
    }
    /// <summary>
    /// EVent to handle updated status text
    /// </summary>
    /// <param name="sender">unused</param>
    /// <param name="e">unused</param>
    void BRMgr_StatusUpdated(object sender, EventArgs e)
    {
        BuildResultsManager.BuildResultsManager brm;
        brm = (BuildResultsManager.BuildResultsManager)sender;
        status.Text = brm.StatusMessage;
    }
}

如果您不使用 GUI,则无需在同一线程上执行操作,只需使用锁定机制(锁、互斥锁等)保护非线程安全资源以避免并发访问,您就很好。

最新更新