饭太稀
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Fantasy
|
||||
{
|
||||
internal interface ISceneScheduler : IDisposable
|
||||
{
|
||||
void Add(Scene scene);
|
||||
void Remove(Scene scene);
|
||||
void Update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
#if FANTASY_UNITY || FANTASY_NET || !FANTASY_WEBGL
|
||||
using System.Threading;
|
||||
#endif
|
||||
#if FANTASY_NET
|
||||
using Fantasy.Platform.Net;
|
||||
#endif
|
||||
namespace Fantasy
|
||||
{
|
||||
internal sealed class MainScheduler : ISceneScheduler
|
||||
{
|
||||
private readonly Queue<Scene> _queue = new Queue<Scene>();
|
||||
public readonly ThreadSynchronizationContext ThreadSynchronizationContext;
|
||||
|
||||
public MainScheduler()
|
||||
{
|
||||
ThreadSynchronizationContext = new ThreadSynchronizationContext();
|
||||
#if !FANTASY_WEBGL
|
||||
SynchronizationContext.SetSynchronizationContext(ThreadSynchronizationContext);
|
||||
#endif
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_queue.Clear();
|
||||
}
|
||||
|
||||
public void Add(Scene scene)
|
||||
{
|
||||
ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
if (scene.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(scene);
|
||||
});
|
||||
}
|
||||
|
||||
public void Remove(Scene scene)
|
||||
{
|
||||
ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
if (scene.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var initialCount = _queue.Count;
|
||||
for (var i = 0; i < initialCount; i++)
|
||||
{
|
||||
var currentScene = _queue.Dequeue();
|
||||
if (currentScene != scene)
|
||||
{
|
||||
_queue.Enqueue(currentScene);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ThreadSynchronizationContext.Update();
|
||||
var initialCount = _queue.Count;
|
||||
|
||||
while (initialCount-- > 0)
|
||||
{
|
||||
if(!_queue.TryDequeue(out var scene))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scene.IsDisposed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
scene.Update();
|
||||
_queue.Enqueue(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#if !FANTASY_WEBGL || !FANTASY_SINGLETHREAD
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
namespace Fantasy
|
||||
{
|
||||
internal struct MultiThreadStruct : IDisposable
|
||||
{
|
||||
public readonly Thread Thread;
|
||||
public readonly CancellationTokenSource Cts;
|
||||
|
||||
public MultiThreadStruct(Thread thread, CancellationTokenSource cts)
|
||||
{
|
||||
Thread = thread;
|
||||
Cts = cts;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cts.Cancel();
|
||||
if (Thread.IsAlive)
|
||||
{
|
||||
Thread.Join();
|
||||
}
|
||||
Cts.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MultiThreadScheduler : ISceneScheduler
|
||||
{
|
||||
private bool _isDisposed;
|
||||
private readonly ConcurrentDictionary<long, MultiThreadStruct> _threads = new ConcurrentDictionary<long, MultiThreadStruct>();
|
||||
public int ThreadCount => _threads.Count;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
foreach (var (_, multiThreadStruct) in _threads.ToArray())
|
||||
{
|
||||
multiThreadStruct.Dispose();
|
||||
}
|
||||
|
||||
_threads.Clear();
|
||||
}
|
||||
|
||||
public void Add(Scene scene)
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
var thread = new Thread(() => Loop(scene, cts.Token));
|
||||
_threads.TryAdd(scene.RuntimeId, new MultiThreadStruct(thread, cts));
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public void Remove(Scene scene)
|
||||
{
|
||||
if (_threads.TryRemove(scene.RuntimeId, out var multiThreadStruct))
|
||||
{
|
||||
multiThreadStruct.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void Loop(Scene scene, CancellationToken cancellationToken)
|
||||
{
|
||||
var sceneThreadSynchronizationContext = scene.ThreadSynchronizationContext;
|
||||
SynchronizationContext.SetSynchronizationContext(sceneThreadSynchronizationContext);
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (scene.IsDisposed)
|
||||
{
|
||||
Remove(scene);
|
||||
return;
|
||||
}
|
||||
|
||||
sceneThreadSynchronizationContext.Update();
|
||||
scene.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error in MultiThreadScheduler loop: {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
#if !FANTASY_WEBGL || !FANTASY_SINGLETHREAD
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
namespace Fantasy
|
||||
{
|
||||
internal sealed class ThreadPoolScheduler : ISceneScheduler
|
||||
{
|
||||
private bool _isDisposed;
|
||||
private readonly List<Thread> _threads;
|
||||
private readonly ConcurrentBag<Scene> _queue = new ConcurrentBag<Scene>();
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
public ThreadPoolScheduler()
|
||||
{
|
||||
// 最大线程数、避免线程过多发生的资源抢占问题。
|
||||
// 但如果使用了MultiThreadScheduler,那么这里的线程数就算是设置了也有可能导致线程过多的问题。
|
||||
// 线程过多看每个线程的抢占情况,如果抢占资源占用不是很大也没什么大问题。如果过大的情况,就会有性能问题。
|
||||
// 所以根据情况来使用不同的调度器。
|
||||
var maxThreadCount = Environment.ProcessorCount;
|
||||
_threads = new List<Thread>(maxThreadCount);
|
||||
|
||||
for (var i = 0; i < maxThreadCount; ++i)
|
||||
{
|
||||
Thread thread = new(() => Loop(_cancellationTokenSource.Token))
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
_threads.Add(thread);
|
||||
thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
foreach (var thread in _threads)
|
||||
{
|
||||
if (thread.IsAlive)
|
||||
{
|
||||
thread.Join();
|
||||
}
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Dispose();
|
||||
_threads.Clear();
|
||||
}
|
||||
|
||||
public void Add(Scene scene)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Add(scene);
|
||||
}
|
||||
|
||||
public void Remove(Scene scene)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newQueue = new Queue<Scene>();
|
||||
|
||||
while (!_queue.IsEmpty)
|
||||
{
|
||||
if (_queue.TryTake(out var currentScene))
|
||||
{
|
||||
if (currentScene != scene)
|
||||
{
|
||||
newQueue.Enqueue(currentScene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (newQueue.TryDequeue(out var newScene))
|
||||
{
|
||||
_queue.Add(newScene);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void Loop(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (_queue.TryTake(out var scene))
|
||||
{
|
||||
if (scene == null || scene.IsDisposed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sceneThreadSynchronizationContext = scene.ThreadSynchronizationContext;
|
||||
SynchronizationContext.SetSynchronizationContext(sceneThreadSynchronizationContext);
|
||||
|
||||
try
|
||||
{
|
||||
sceneThreadSynchronizationContext.Update();
|
||||
scene.Update();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error in ThreadPoolScheduler scene: {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(null);
|
||||
}
|
||||
|
||||
_queue.Add(scene);
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 当队列为空的时候、避免无效循环消耗CPU。
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
namespace Fantasy
|
||||
{
|
||||
/// <summary>
|
||||
/// 线程调度器
|
||||
/// </summary>
|
||||
internal static class ThreadScheduler
|
||||
{
|
||||
/// <summary>
|
||||
/// 主线程调度器
|
||||
/// </summary>
|
||||
public static MainScheduler MainScheduler { get; private set; }
|
||||
/// <summary>
|
||||
/// 多线程调度器,根据当前CPU核心数量创建的固定线程。
|
||||
/// </summary>
|
||||
public static ISceneScheduler MultiThreadScheduler { get; private set; }
|
||||
/// <summary>
|
||||
/// 线程池调度器
|
||||
/// </summary>
|
||||
public static ISceneScheduler ThreadPoolScheduler { get; private set; }
|
||||
|
||||
static ThreadScheduler()
|
||||
{
|
||||
MainScheduler = new MainScheduler();
|
||||
}
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
MainScheduler.Update();
|
||||
}
|
||||
|
||||
internal static void AddMainScheduler(Scene scene)
|
||||
{
|
||||
MainScheduler.Add(scene);
|
||||
}
|
||||
|
||||
internal static void AddToMultiThreadScheduler(Scene scene)
|
||||
{
|
||||
if (MultiThreadScheduler == null)
|
||||
{
|
||||
#if FANTASY_SINGLETHREAD || FANTASY_WEBGL
|
||||
MultiThreadScheduler = MainScheduler;
|
||||
#else
|
||||
MultiThreadScheduler = new MultiThreadScheduler();
|
||||
#endif
|
||||
}
|
||||
|
||||
MultiThreadScheduler.Add(scene);
|
||||
}
|
||||
|
||||
internal static void AddToThreadPoolScheduler(Scene scene)
|
||||
{
|
||||
if (ThreadPoolScheduler == null)
|
||||
{
|
||||
#if FANTASY_SINGLETHREAD || FANTASY_WEBGL
|
||||
ThreadPoolScheduler = MainScheduler;
|
||||
#else
|
||||
ThreadPoolScheduler = new ThreadPoolScheduler();
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadPoolScheduler.Add(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user