簡體   English   中英

沒有C#中的GUI的DLL中引發跨線程事件

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

我寫了一個DLL,可以完成很多工作。 它要做的一件事是在大量傳入消息中搜索特定消息,清理消息,並在找到消息時引發事件,然后通過EventArgs傳遞數據。

當前,DLL正在工作,但是搜索消息的例程正在減慢一切,並使系統響應速度變慢,因為要處理的數據太多。

我想將搜索移到它自己的線程上,一旦找到消息,它就會在主線程上引發一個事件,並將消息數據傳遞給主線程。 我知道如何使線程完成工作,但是我不知道如何使線程引發主線程上的事件並將數據傳遞給它。 有人可以告訴我該怎么做,還是要舉一個例子?

我嘗試創建一個委托並觸發一個事件,該方法可以正常工作,但是當我嘗試傳遞數據時,出現了跨線程異常。

一些細節可能很重要。 極有可能根本沒有GUI,並且可能仍然是控制台應用程序。 消息將始終進入,並且DLL必須不斷搜索它們。 DLL正在尋找的消息可能每秒出現1兩次,或者消息之間可能間隔幾天。

更新一個真正簡單的項目,說明我想做什么。 這段代碼很粗糙,因為我將其組合在一起進行了測試。

如果您遵循該代碼,則將看到一切正常,直到CollectData函數中的行:

StatusUpdated(this, null);

叫做。 此時,由於UI線程和數據收集線程,它會導致跨線程異常。 我知道我可以通過在UI線程上使用調用來解決此問題,但是如上所述,它很可能不會與任何類型的GUI一起使用(但它應該能夠處理它)。 因此,跨線程異常需要在BuildResultsManager類中修復,我認為是在函數BRMgr_StatusUpdated中。 如何在不更改MainWindow類中的任何代碼的情況下將數據(在本例中為currentVal的值)獲取到UI線程。

您可以通過創建包含2個項目的解決方案來運行此程序,其中1個用於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,則無需在同一線程上執行操作,只需使用鎖定機制(鎖,互斥鎖等)保護非線程安全資源,以避免並發訪問,那么您就很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM