在過去的幾個月里,我一直在努力改進我使用 DispatcherTimer 定期檢查資源以查看它們是否需要更新/處理的程序。更新資源(“產品”)后,將產品移至流程的下一步等。資源可能立即可用,也可能不可用。
我一直在掙扎的原因有兩個。一個原因是我想異步實作這個程序,因為它目前只是同步的。第二個原因是我已經確定了我的實作卡住的區域,這似乎不是一個不常見的設計模式,但我不知道如何簡潔地描述它,所以我無法弄清楚如何從谷歌那里得到有用的答案.
一個相當重要的注意事項是我通過直接 USB 連接訪問這些產品,因此我使用 LibUsbDotNet 與設備連接。我使 USB 連接異步,因此我可以同時連接到多個產品并一次處理任意數量。
public Class Product
{
public bool IsSoftwareUpdated = false;
public bool IsProductInformationCorrect = false;
public bool IsEOLProcessingCompleted = false;
public Product(){}
~Product()
}
public class ProcessProduct
{
List<Product> bagOfProducts = new List<Product>(new Product[10]);
ConcurrentBag<Product> UnprocessedUnits = new ConcurrentBag<Product>();
ConcurrentBag<Product> CurrentlyUpdating = new ConcurrentBag<Product>();
ConcurrentBag<Product> CurrentlyVerifyingInfo = new ConcurrentBag<Product>();
ConcurrentBag<Product> FinishedProcessing = new ConcurrentBag<Product>();
DispatcherTimer _timer = new DispatcherTimer();
public ProcessProduct()
{
_timer.Tick = Timer_Tick; //Every 1 second, call Timer_Tick
_timer.Interval = new TimeSpan(0,0,1); //1 Second timer
bagOfProducts.ForEach(o => UnprocessedUnits.Add(o)); //Fill the UnprocessedUnits with all products
StartProcessing();
}
private void StartProcessing()
{
_timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
ProductOrganizationHandler();
foreach(Product prod in CurrentlyUpdating.ToList())
{
UpdateProcessHandler(prod); //Async function that uses await
}
foreach(Product prod in CurrentlyVerifyingInfo.ToList())
{
VerifyingInfoHandler(prod); //Async function that uses Await
}
if(FinishedProcessing.Count == bagOfProducts.Count)
{
_timer.Stop(); //If all items have finished processing, then stop the process
}
}
private void ProductOrganizationHandler()
{
//Take(read REMOVE) Product from each ConcurrentBag 1 by 1 and moves that item to the bag that it needs to go
//depending on which process step is finished
//(or puts it back in the same bag if that step was not finished).
//E.G, all items are moved from UnprocessUnits to CurrentlyUpdating or CurrentlyVerifying etc.
//If a product is finished updating, it is moved from CurrentlyUpdating to CurrentlyVerifying or FinishedProcessing
}
private async void UpdateProcessHandler(Product prod)
{
await Task.Delay(1000).ConfigureAwait(false);
//Does some actual work validating USB communication and then running through the USB update
}
private async void VerifyingInfoHandler(Product prod)
{
await Task.Delay(1000).ConfigureAwait(false);
//Does actual work here and communicates with the product via USB
}
}
完整的可編譯代碼示例可通過我在 Pastebin 上的代碼獲得。
所以,我的問題真的是這樣的:這段代碼中是否有任何有意義的競爭條件?具體而言,與ProductOrganizationHandler()代碼和通過ConcurrentBags在回圈Timer_Tick() (因為到一個新的呼叫Timer_Tick()發生每秒)。我確信這段代碼大部分時間都可以作業,但我擔心稍后會由于罕見的競爭條件而發生難以跟蹤的錯誤,例如ProductOrganizationHandler()需要 > 1 秒才能運行一些愚蠢的理由。
作為次要說明:這是否是此類流程的最佳設計模式?C# 是我的第一個 OOP 語言,并且都是在作業中自學的(我幾乎所有的作業都是嵌入式 C),所以我對 OOP 設計模式沒有任何正式經驗。
My main goal is to asynchronously Update/Verify/Communicate with each device as it becomes available via USB. Once all products in the list are finished (or a timeout), then the process finishes. This project is in .NET 5.
EDIT: For anyone that comes along later with the same question, here's what I did.
I did not understand that DispatcherTimer add Ticks to the Dispatcher queue. This implies that a tick will only run if there is not already another instance of Tick already running or, worded another way, Timer_Tick will run to completion before the next Timer_Tick instance runs.
So, most(all?) of the Threading/concurrency concerns I had were unfounded and I can treat the Timer_Tick as a single-threaded non-concurrent function (which it is).
此外,為了保持蜱從堆積如山,我跑_timer.Stop()之初Timer_Tick并重新啟動定時器在年底Timer_Tick。
uj5u.com熱心網友回復:
首先,您使用的是DispatchTimer,這會在 UI 執行緒上引起滴答聲。因此,據我所知,示例中沒有進行多執行緒。還有其他計時器,System.Timers.Timer如果這是意圖,它會在后臺執行緒上引發事件。但是,如果您只想經常檢查和更新狀態,并且不想運行任何阻塞的代碼,那么僅使用 UI 執行緒就可以了,并且會大大簡化事情。
即使我們假設ProductOrganizationHandler確實在作業執行緒上運行,從一個并發集合中洗掉專案并將它們放在另一個中通常仍然是安全的。但它不能保證以任何特定順序處理專案,也不保證任何特定專案由計時器的給定滴答處理。但是由于計時器會定期打勾,因此最終應該處理所有專案。請記住,大多數計時器都需要處理,因此您需要以某種方式處理它,包括處理是否過早停止。
請記住,async這并不意味著并發,因此除非您的 USB 庫提供異步方法,否則我不會使用它。即便如此,我也會避免,async void因為這會引發捕獲的同步背景關系的例外,可能會導致應用程式崩潰,因此它應該主要用于最外層,例如按鈕事件處理程式或計時器,然后您可能應該以某種方式處理例外。
至于最好的方法,我會看看DataFlow庫。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/348868.html
