简体   繁体   English

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

[英]raising cross thread events in a DLL without a GUI in C#

I've written a DLL that does a bunch of stuff. 我写了一个DLL,可以完成很多工作。 One of the things it does is to search through a lot of incoming messages for a specific message, clean up the message, and raise an event when the message is found, and pass the data via the EventArgs. 它要做的一件事是在大量传入消息中搜索特定消息,清理消息,并在找到消息时引发事件,然后通过EventArgs传递数据。

Currently the DLL is working but the routine that searches for the message is slowing everything down and making the system slow to respond because there is so much data to go through. 当前,DLL正在工作,但是搜索消息的例程正在减慢一切,并使系统响应速度变慢,因为要处理的数据太多。

I would like to move this searching to it's own thread, and once the message is found have it raise an event on the main thread and pass the message data to the main thread. 我想将搜索移到它自己的线程上,一旦找到消息,它就会在主线程上引发一个事件,并将消息数据传递给主线程。 I know how to make a thread to do the work, but I do not know how to make the thread raise the event on the main thread and pass the data to it. 我知道如何使线程完成工作,但是我不知道如何使线程引发主线程上的事件并将数据传递给它。 Could someone tell me how to do this, or point to an example of it? 有人可以告诉我该怎么做,还是要举一个例子?

I've tried creating a delegate and triggering an event, which works but when I try to pass the data I get a cross thread exception. 我尝试创建一个委托并触发一个事件,该方法可以正常工作,但是当我尝试传递数据时,出现了跨线程异常。

Some details that may be important. 一些细节可能很重要。 There most likely won't be a GUI at all and will probably remain a console application. 极有可能根本没有GUI,并且可能仍然是控制台应用程序。 Messages will always be coming in and the DLL has to continuously search them. 消息将始终进入,并且DLL必须不断搜索它们。 The message the DLL is looking for may come as often as 1 couple times a second or it may be days between the messages. DLL正在寻找的消息可能每秒出现1两次,或者消息之间可能间隔几天。

Update for a real simple project illustrating what I would like to do. 更新一个真正简单的项目,说明我想做什么。 This code is rough cause I threw it together to test this out. 这段代码很粗糙,因为我将其组合在一起进行了测试。

If you follow the code you will see that everything runs fine until the line in the CollectData function where: 如果您遵循该代码,则将看到一切正常,直到CollectData函数中的行:

StatusUpdated(this, null);

is called. 叫做。 At this point it causes the cross thread exception because of the UI thread and the data Collection thread. 此时,由于UI线程和数据收集线程,它会导致跨线程异常。 I know I can fix this by using an invoke on the UI thread, but as mentioned above this will most likely not be used with any sort of GUI (but it should be able to handle it). 我知道我可以通过在UI线程上使用调用来解决此问题,但是如上所述,它很可能不会与任何类型的GUI一起使用(但它应该能够处理它)。 So the cross thread exception needs to be fixed in the BuildResultsManager class, I think in the function BRMgr_StatusUpdated. 因此,跨线程异常需要在BuildResultsManager类中修复,我认为是在函数BRMgr_StatusUpdated中。 How do I get the data (in this case the value of currentVal) to the UI thread without changing any code in the MainWindow class. 如何在不更改MainWindow类中的任何代码的情况下将数据(在本例中为currentVal的值)获取到UI线程。

You can run this by creating a solution with 2 projects, 1 for the first 2 files as a dll. 您可以通过创建包含2个项目的解决方案来运行此程序,其中1个用于dll的前两个文件。 The second as a wpf project, referencing the dll, and put a textbox named status on the form. 第二个作为wpf项目,引用dll,并在窗体上放置一个名为status的文本框。

Main Class: 主类:

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

}
}

The class that will be doing all of the thread work: 将执行所有线程工作的类:

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

Code behind for the wpf project: 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,则无需在同一线程上执行操作,只需使用锁定机制(锁,互斥锁等)保护非线程安全资源,以避免并发访问,那么您就很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM