我已經寫了一個做一堆東西的DLL。它所做的一件事是搜索特定消息的大量傳入消息,清理消息,並在找到消息時引發事件,並通過EventArgs傳遞數據。在C#中沒有GUI的DLL中引發交叉線程事件#
當前DLL正在工作,但搜索消息的例程正在放緩所有操作,並使系統響應速度變慢,因爲有太多數據要通過。
我想將這個搜索移到它自己的線程中,一旦發現消息,它會在主線程上引發一個事件並將消息數據傳遞給主線程。我知道如何創建一個線程來完成這項工作,但我不知道如何讓線程在主線程上引發事件並將數據傳遞給它。有人能告訴我如何做到這一點,或指出一個例子嗎?
我試着創建一個委託並觸發一個事件,這工作,但是當我嘗試傳遞數據時,我得到一個交叉線程異常。
一些可能很重要的細節。很可能根本不會是GUI,並且可能仍然是控制檯應用程序。消息總是會進來,並且DLL必須不斷地搜索它們。 DLL正在查找的消息可能會每秒多達幾次,或者可能是消息之間的幾天。
更新一個真正簡單的項目,說明我想要做什麼。這段代碼很粗糙,因爲我把它扔在一起測試了這一點。
如果按照代碼,你會發現一切都正常運行,直到CollectData功能線,其中:
StatusUpdated(this, null);
被調用。此時,由於UI線程和數據收集線程,它會導致交叉線程異常。我知道我可以通過在UI線程上調用來解決這個問題,但如上所述,這很可能不會用於任何類型的GUI(但它應該能夠處理它)。因此,需要在BuildResultsManager類中修復交叉線程異常,我認爲在函數BRMgr_StatusUpdated中。如何在不更改MainWindow類中的任何代碼的情況下將數據(在本例中爲currentVal的值)獲取到UI線程。
你可以通過創建一個包含2個項目的解決方案來運行這個解決方案,前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;
}
}
因爲沒有數據被.DLL保存,所以永遠不會有併發訪問。只有DLL才能轉換傳入的消息,然後傳遞給它。該DLL沒有理由存儲數據。我遇到的問題是如何將此數據從執行搜索的線程傳遞到可能使用它的主線程。使用鎖,互斥鎖等意味着主線程需要循環查看是否有新的數據存儲。我想有一個事件通知主線程有數據。 – TTalma
不,你沒有明白我的意思,你只是從輔助線程中引發一個事件,它會被附加的類接收,*那裏*是你必須保護資源以進行併發訪問的地方。 – Gusman
我想我還是有點困惑。我更新了一個項目,我打了一個項目,說明了我正在嘗試做什麼。最後,我不知道DLL將如何使用,它可以與GUI一起使用。但主要是現有的程序無法訪問源代碼,並且此dll必須與其替換的程序兼容。 – TTalma