饭太稀

This commit is contained in:
bob
2025-06-30 10:51:37 +08:00
commit 8e45469c83
753 changed files with 87652 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
using System;
namespace Fantasy
{
internal interface ISceneScheduler : IDisposable
{
void Add(Scene scene);
void Remove(Scene scene);
void Update();
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
}