Um was geht es in dem Artikel
In diesem Artikel werden wir über serverseitiges Multithreading sprechen.
Wie wird es umgesetzt?
wie wird verwendet
was kann getan werden
was wir selbst erfunden haben
All diese Fragen sind nur relevant, wenn Sie etwas direkt für den Server entwickeln - den SDK-Code ändern, Ihr eigenes Plugin schreiben oder sogar etwas Eigenes tun.
Wie geht Photon mit Multithreading um?
Die Photon-Server-Anwendung akzeptiert Anforderungen von mehreren Client-Verbindungen. Ich werde solche Verbindungen Peers nennen . Diese Anforderungen bilden Warteschlangen. Eine für jedes Fest. Wenn Peers mit demselben Raum verbunden sind, werden ihre Warteschlangen zu einer zusammengefasst - der Raumwarteschlange. Es gibt bis zu mehreren tausend solcher Räume, und ihre Anforderungswarteschlangen werden ebenfalls parallel verarbeitet.
Als Grundlage für die Implementierung von Task-Warteschlangen in Photon wurde die Retlang-Bibliothek verwendet, die auf Basis der Jetlang-Bibliothek entwickelt wurde.
Warum nicht Task und async / await verwenden?
Daher gibt es die folgenden Überlegungen:
Photon begann sich zu entwickeln, bevor diese Dinge auftauchten.
, , - . , , , GC , . , .
TaskScheduler, , - .
Fiber?
, . - FIFO. , multiple writers-single reader. , , , .. . .
Photon , PoolFiber, . IFiber. .
ThreadFiber - IFiber, . .
PoolFiber - IFiber, .NET. . . ( ).
FormFiber/DispatchFiber - IFiber, WinForms/WPF. FormFiber/DispatchFiber Invoke BeginInvoke .
StubFiber - . , (races) . .
PoolFiber
PoolFiber. , . :
. ThreadPool.QueueUserWorkItem. - .
, , ThreadPool.QueueUserWorkItem, . . , .
.. , , . , , () . - , , .
PoolFiber
Photon PoolFiber. , . , , . .. PoolFiber.Stop . .
. -. , , , .
:
,
, .
:
//
fiber.Enqueue(()=>{some action code;});
// , 10
var scheduledAction = fiber.Schedule(()=>{some action code;}, 10_000);
...
//
scheduledAction.Dispose()
// , 10 5
var scheduledAction = fiber.Schedule(()=>{some action code;}, 10_000, 5_000);
...
//
scheduledAction.Dispose()
, - , fiber.Schedule. .
Executors
. , . Execute(Action a)
Execute(List<Action> a)
. PoolFiber . .. . . DefaultExecutor. :
public void Execute(List<Action> toExecute)
{
foreach (var action in toExecute)
{
Execute(action);
}
}
public void Execute(Action toExecute)
{
if (_running)
{
toExecute();
}
}
. 'action' toExecute . FailSafeBatchExecutor, try/catch. , . , , github.
BeforeAfterExecutor
, . BeforeAfterExecutor. "" . , FailSafeBatchExecutor. BeforeAfterExecutor . :
public BeforeAfterExecutor(Action beforeExecute, Action afterExecute, IExecutor executor = null)
. . . / , , . , .
:
var beforeAction = ()=>
{
log4net.ThreadContext.Properties["Meta1"] = "value";
};
var afterAction = () => ThreadContext.Properties.Clear();
//
var e = new BeforeAfterExecutor(beforeAction, afterAction);
// PoolFiber
var fiber = new PoolFiber(e);
- , fiber, log4net Meta1 value.
ExtendedPoolFiber ExtendedFailSafeExecutor
, retlang, . . , . . PoolFiber ( , .NET). , , HTTP . :
event;
, , , , event ;
event.
. , , , , event. . . , .
ExtendedPoolFiber ExtendedFailSafeExecutor. . , . , Pause. ( ) . :
Resume
( Pause) Resume , , .
, , HTTP . , . Resume , .
, ThreadFiber .
IFiberAction
IFiberAction - GC. .NET. , IFiberAction. , . GC
IFiberAction :
public interface IFiberAction
{
void Execute()
void Return()
}
Execute , . Return Execute, .
:
public class PeerHandleRequestAction : IFiberAction
{
public static readonly ObjectPool<PeerHandleRequestAction> Pool = initialization;
public OperationRequest Request {get; set;}
public PhotonPeer Peer {get; set;}
public void Execute()
{
this.Peer.HandleRequest(this.Request);
}
public void Return()
{
this.Peer = null;
this.Request = null;
Pool.Return(this);
}
}
//
var action = PeerHandleRequestAction.Pool.Get();
action.Peer = peer;
action.Request = request;
peer.Fiber.Enqueue(action);
, . , . , PoolFiber . PoolFiber .NET. PoolFiber , . , ExtendedPoolFiber.
Ausführende, die die IExecutor- Schnittstelle implementieren , sind direkt an der Ausführung von Aufgaben in Fasern beteiligt . DefaultExecutor ist für alle gut, verliert jedoch im Falle einer Ausnahme den gesamten Rest der Aufgaben, die zur Ausführung an ihn übertragen wurden. FailSafeExecutor scheint in dieser Hinsicht eine kluge Wahl zu sein . Wenn Sie vor und nach der Ausführung einer Reihe von Aufgaben durch den Executor eine Aktion ausführen müssen, kann BeforeAfterExecutor hilfreich sein