我有一個需要呼叫 COM 物件的 .NET 應用程式(它總是必須從同一個執行緒呼叫)。由于我在應用程式中有多個執行緒,因此我需要在另一個執行緒上呼叫一個操作。
該應用程式沒有(標準)訊息回圈,我真的不喜歡添加 WPF / WinForms 只是為了擁有一個Dispatcher.
什么是實作自定義“訊息回圈”/佇列的安全有效方法,允許在另一個執行緒上呼叫Action/ (帶有回傳型別)?
為這個問題提供一個跨平臺的解決方案也很好。Func
uj5u.com熱心網友回復:
根據@theodor-zoulias 的資訊,我想出了這個解決方案。
免責宣告:可能這實際上是一個非常糟糕的設計!
public sealed class DispatcherLoop : IDisposable
{
#region Instance
private DispatcherLoop() { }
static Dictionary<int, DispatcherLoop> dispatcherLoops = new();
public static DispatcherLoop Current
{
get
{
int threadId = Thread.CurrentThread.ManagedThreadId;
if (dispatcherLoops.ContainsKey(threadId))
return dispatcherLoops[threadId];
DispatcherLoop dispatcherLoop = new()
{
ThreadId = Thread.CurrentThread.ManagedThreadId
};
dispatcherLoops.Add(threadId, dispatcherLoop);
return dispatcherLoop;
}
}
#endregion
bool isDisposed = false;
public void Dispose()
{
if (isDisposed)
throw new ObjectDisposedException(null);
_queue.CompleteAdding();
_queue.Dispose();
dispatcherLoops.Remove(ThreadId);
isDisposed = true;
}
public int ThreadId { get; private set; } = -1;
public bool IsRunning { get; private set; } = false;
BlockingCollection<Task> _queue = new();
public void Run()
{
if (isDisposed)
throw new ObjectDisposedException(null);
if (ThreadId != Thread.CurrentThread.ManagedThreadId)
throw new InvalidOperationException($"The {nameof(DispatcherLoop)} has been created for a different thread!");
if (IsRunning)
throw new InvalidOperationException("Already running!");
IsRunning = true;
try
{
// ToDo: `RunSynchronously` is not guaranteed to be executed on this thread (see comments below)!
foreach (var task in _queue.GetConsumingEnumerable())
task?.RunSynchronously();
}
catch (ObjectDisposedException) { }
IsRunning = false;
}
public void BeginInvoke(Task task)
{
if (isDisposed)
throw new ObjectDisposedException(null);
if (!IsRunning)
throw new InvalidOperationException("Not running!");
if (ThreadId == Thread.CurrentThread.ManagedThreadId)
task?.RunSynchronously();
else
_queue.Add(task);
}
public void Invoke(Action action)
{
if (isDisposed)
throw new ObjectDisposedException(null);
Task task = new(action);
BeginInvoke(task);
task.GetAwaiter().GetResult();
}
public T Invoke<T>(Func<T> action)
{
if (isDisposed)
throw new ObjectDisposedException(null);
Task<T> task = new(action);
BeginInvoke(task);
return task.GetAwaiter().GetResult();
}
}
uj5u.com熱心網友回復:
您應該使用 Microsoft 的反應式框架(又名 Rx) - NuGetSystem.Reactive并添加using System.Reactive.Linq;- 然后您可以這樣做:
var els = new EventLoopScheduler();
然后你可以做這樣的事情:
els.Schedule(() => Console.WriteLine("Hello, World!"));
它EventLoopScheduler啟動自己的執行緒,您可以要求它安排您喜歡的任何作業 - 它永遠是那個執行緒。
完成調度程式后,只需呼叫els.Dispose()即可干凈地將其關閉。
未來還有很多用于調度代碼的多載。這是一個非常強大的類。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/430567.html
