c#在其他线程上执行代码
本文关键字:执行 代码 线程 其他 | 更新日期: 2023-09-27 18:18:18
我在应用程序中的线程有一些麻烦。我有一个多线程客户机/服务器应用程序。我也使用c# MonoDevelop为Unity3d。不确定这对答案有什么影响。我试着解释我的问题在哪里:
Unity工作在一个线程上。所以如果我想实例化一个使用unity抽象类ScriptableObject
的对象,那么这必须在unity运行的主线程上完成。
但是我的服务器套接字为每个连接的客户端生成一个线程,因此传入的数据可以异步处理。接收到的数据在OnDataReceived()
方法中处理(它在自己的线程上运行)
这里的问题是,我不能在OnDataReceived()
线程内创建Player
对象的实例。因为我的Player
对象继承自ScriptableObject
。这意味着这个对象应该在Unity主线程上创建。
但是我不知道该怎么做……是否有一种方法可以切换回主线程,所以我仍然可以在OnDataReceived()
方法中创建Player
对象?
。. NET已经有了一个SynchronizationContext
的概念,最常用于UI应用程序,其中需要线程关联来调用UI控件上的操作(例如在WPF或WinForms中)。然而,即使在UI应用程序之外,你也可以将这些概念重用为通用的线程关联工作队列。
这个示例展示了如何在一个简单的控制台应用程序中使用WPF DispatcherSynchronizationContext
(来自WindowsBase.dll
),以及。net 4.0任务类(TaskScheduler
/Task
)来在主程序线程中调用源自子线程的操作。
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
internal sealed class Program
{
private static void Main(string[] args)
{
int threadCount = 2;
using (ThreadData data = new ThreadData(threadCount))
{
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; ++i)
{
threads[i] = new Thread(DoOperations);
}
foreach (Thread thread in threads)
{
thread.Start(data);
}
Console.WriteLine("Starting...");
// Start and wait here while all work is dispatched.
data.RunDispatcher();
}
// Dispatcher has exited.
Console.WriteLine("Shutdown.");
}
private static void DoOperations(object objData)
{
ThreadData data = (ThreadData)objData;
try
{
// Start scheduling operations from child thread.
for (int i = 0; i < 5; ++i)
{
int t = Thread.CurrentThread.ManagedThreadId;
int n = i;
data.ExecuteTask(() => SayHello(t, n));
}
}
finally
{
// Child thread is done.
data.OnThreadCompleted();
}
}
private static void SayHello(int requestingThreadId, int operationNumber)
{
Console.WriteLine(
"Saying hello from thread {0} ({1}) on thread {2}.",
requestingThreadId,
operationNumber,
Thread.CurrentThread.ManagedThreadId);
}
private sealed class ThreadData : IDisposable
{
private readonly Dispatcher dispatcher;
private readonly TaskScheduler scheduler;
private readonly TaskFactory factory;
private readonly CountdownEvent countdownEvent;
// In this example, we initialize the countdown event with the total number
// of child threads so that we know when all threads are finished scheduling
// work.
public ThreadData(int threadCount)
{
this.dispatcher = Dispatcher.CurrentDispatcher;
SynchronizationContext context =
new DispatcherSynchronizationContext(this.dispatcher);
SynchronizationContext.SetSynchronizationContext(context);
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
this.factory = new TaskFactory(this.scheduler);
this.countdownEvent = new CountdownEvent(threadCount);
}
// This method should be called by a child thread when it wants to invoke
// an operation back on the main dispatcher thread. This will block until
// the method is done executing.
public void ExecuteTask(Action action)
{
Task task = this.factory.StartNew(action);
task.Wait();
}
// This method should be called by threads when they are done
// scheduling work.
public void OnThreadCompleted()
{
bool allThreadsFinished = this.countdownEvent.Signal();
if (allThreadsFinished)
{
this.dispatcher.InvokeShutdown();
}
}
// This method should be called by the main thread so that it will begin
// processing the work scheduled by child threads. It will return when
// the dispatcher is shutdown.
public void RunDispatcher()
{
Dispatcher.Run();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
// Dispose all IDisposable resources.
private void Dispose(bool disposing)
{
if (disposing)
{
this.countdownEvent.Dispose();
}
}
}
}
样本输出:<>之前开始……从线程1上的线程3(0)问好。从线程1上的线程4(0)问好。从线程3(1)在线程1上问好。从线程1上的线程4(1)问好。从线程3(2)在线程1上问好。从线程4(2)在线程1上问好。从线程1上的线程3(3)问好。从线程1上的线程4(3)问好。从线程3(4)在线程1上问好。从线程1上的线程4(4)问好。关闭。 您可以通过诸如
之类的类与原始线程通信。class Communicator
{
public static volatile bool CreatePlayer;
}
在套接字代码中,更改CreatePlayer变量。在接收器代码中,检查变量并创建一个播放器。之后,将CreatePlayer设置为false。其他事情也一样。在同时跨两个线程操作一个变量时要小心——例如,CreatePlayer有四个布尔值可能比NumPlayersToCreate有一个int值更好,这样两个线程就不会试图不断地访问相同的数据。当然,你得侧写才能知道。最后一件事:确保跨两个线程更改的变量被标记为volatile。这使得每个线程从主存访问数据,而不是将其保存在缓存中(否则,每个线程都不会注意到数据在其他线程的缓存中被更改)。
是的,这不是性能最好或最优雅的解决方案,但它是最简单的。我肯定有人会提出更复杂的建议;如果你愿意,我也可以这样做。但是,您似乎不熟悉多线程,所以我认为您需要一些简单的东西来开始。