SlideShare a Scribd company logo
C# Server 만들기
2013. 06. 07.
최재영
Why?
• 빌드 속도
• 표현력
2
async
await
extension
method
linq
Observable
TPL
DynamicObject
Reflection
Attribute
IEnumerable
흐름
3
Network
Datasheet
Database
Logic
async, await
TaskCompletionSource
Reflection
Attribute
Dynamic
XmlLinq
IEnumerable
Network
• 빠른 패킷 처리를 위해 비동기 IO 사용
4
var socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.IPv4);
// preprocess socket
var buffer = new byte[4096];
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,
result => {
var received = socket.EndReceive(result);
// process packet
}, null /* state */);
TaskCompletionSource
• C++ Future + C# Task Awaitable Future
5
Network
static Task<int> ReceiveAsync(this Socket sock, byte[] buf, int off, int size)
{
var source = new TaskCompletionSource<int>(sock);
sock.BeginReceive(buf, off, size, SocketFlags.None, state =>
{
try
{
source.SetResult(socket.EndReceive(state));
}
catch (Exception e)
{
source.SetException(e);
}
}, source);
return source.Task;
}
async, await
• Task.Result 비동기 대기(await), 그런 코드가 있는 함수(async)
6
Network
static async Task<byte[]> ReceiveAsync(this Socket socket, int count)
{
var buffer = new byte[count];
var length = 0;
do
{
var num = await ReceiveAsync(socket, buffer, length, count);
if (num == 0)
break;
length += num;
count -= num;
} while (count > 0);
if (length != buffer.Length) throw new IOException("packet is truncated.");
return buffer;
}
async, await
7
Network
async void ReceiveLoop(Socket socket)
{
while (true)
{
var lengthBytes = await socket.ReceiveAsync(sizeof (int));
var packetBytes = await socket.ReceiveAsync(
BitConverter.ToInt32(lengthBytes, 0));
// process packet
var packet = ReadPacket(packetBytes);
_handlerMap[packet.GetType()](packet);
}
}
await하는 지점에 아직 IO signal이 없다면, 해당 Task는 잠시 멈추고,
가용한 다른 Task를 찾아 수행함
Listener (Server)
• ClientSocket을 비동기로 Accept해서,
• 각 Socket마다 비동기로 Packet을 대기해서 처리함
8
Network
var listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
var localEndPoint = new IPEndPoint(IPAddress.Any, Port);
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
var clientSocket = await listener.AcceptAsync();
ReceiveLoop(clientSocket);
}
async method
Summary
• Socket의 Async 계열 함수를 사용 (.NET 내부는 IOCP로 처리)
• async, await은 TaskContinuation의 Syntax sugar
• async, await Keyword로 Callback 없이 편하게 Network 코드 작성
• 그러면서도 Thread Pool에 의한 효율적으로 수행됨
(.NET Thread Pool도 내부에서 IOCP로 관리)
9
Network
Datasheet
• dynamic을 사용한 일반적인 Xml 읽기
• code generator를 사용
10
Xml 작성
Xml Model
구현
Xml Parser
구현
자동 생성 자동 생성
code generator
DynamicObject
XmlDefinition (자동 생성)
general-loader
DynamicObject
• RuntimeType으로 동적으로 멤버 접근 가능
• 올바른 Type의 값을 미리 준비해야 하므로 XmlDefinition 필요
11
Datasheet
public class XmlObject : DynamicObject
{
private readonly Dictionary<string, object> _attributes;
private readonly Dictionary<string, IEnumerable<XmlObject>>
_multipleChildren;
private readonly Dictionary<string, XmlObject> _singleChildren;
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
return TryGetValue(_attributes, binder.Name, out result) ||
TryGetValue(_singleChildren, binder.Name, out result) ||
TryGetValue(_multipleChildren, binder.Name, out result);
}
DynamicObject
• Attribute를 읽을 때 Type 변환을 미리 수행
• dynamic으로 접근하여 Model 없이 접근 가능
12
Datasheet
_attributes = element.Attributes.OfType<XmlAttribute>()
.ToDictionary(e => e.Name,
e => defNode.SelectAttribute(e.Name)
.ReadValue(e.Value));
<?xml version="1.0" encoding="utf-8" ?>
<World>
<Config port="40123"/>
</World>
World.Config@port : int
dynamic world = XmlObject.Load("World.xml", _def);
_listener.Port = world.Config.port;
Summary
• dynamic을 사용하여 코딩 시간 단축(Model, Parser 작성 불필요)
• 오타로 인한 접근 위반은 Runtime에 확인 가능
• XmlDefinition이 필요함(자동 생성 가능)
• 보다 빠른 속도를 원할 경우에는 Model, Parser를 Generate
(IVsSingleFileGenerator)
13
Datasheet
Database
• Reflection과 Attribute 사용으로 일반적인 Bind 구현
• scheme 작업이 불필요할 경우 model 작성만으로 모든 구현 해결 가능
14
Scheme
작성
DataModel
작성
Bind 구현
nosql or generator Reflection
Reflection
• Runtime에 model의 type정보로 scheme를 구축
• 각 데이터의 Serialize/Deserialize 구현 필요 (String과 object 상호 변환)
• 모든 Model 객체를 Xml로 변환
15
Database
new XElement("Objects",
_gameObjects.Values.Select(
obj =>
new XElement("Object",
obj.GetType().GetProperties()
.Where(e => e.CanRead && e.CanWrite)
.Select(e => new XAttribute(e.Name,
SerializeValue(e.PropertyType, e.GetValue(obj, null))
)))));
모든 Property에 대해 출력
출력할 때에는 string으로, 읽을 때에는 다시 object로
Attribute
• Runtime에 접근 가능한 metadata를 코드에 주입
16
Database
[CommandHandler("npc", "새로운 Npc를 생성합니다")]
internal bool SpawnNpc(Entity admin,
[CommandArgument("Npc의 이름")] string npcName,
[CommandArgument("Npc의 X 위치", 0)] double newX,
[CommandArgument("Npc의 Y 위치", 0)] double newY)
{
if (!admin.Has<Pos>())
return false;
var npc = EntityManager.Instance.Create(EntityTemplate.Ids.Npc);
npc.Get<Motion>().Dir = admin.Get<Motion>().Dir;
npc.Get<Nameplate>().Name = npcName;
npc.Get<Pos>().Assign(new Pos {X = newX, Y = newY});
명령어와 설명을 코드에 기록
인자 설명과 기본 값, type을 코드에 기록
Summary
• model 객체의 type 정보를 최대한 사용
• Attribute를 부여하여 가능한 많은 정보를 코드에 주입
(DSL, 문서, 주석 등 외부 정보는 추가 유지 보수가 필요함)
• Dirty나 Lazy를 사용하여 최적화 가능
• 역시 보다 빠른 속도를 원할 경우에는 Code Generate를 사용
(partial class를 사용하여 사용자 코드와 혼합 가능)
17
Database
Logic
• yield return을 사용하여 State Machine 제거
• context 유지를 위한 별도 코딩이 필요 없음
18
행동
A
행동
B
행동
C
1초 뒤
3초 뒤
5초 뒤
Logic
Engine
Logic #1 Logic #2
IEnumerable (Coroutine)
• IEnumerable을 반환 type으로 설정하여 yield return 사용
• 다음 로직 수행까지의 대기 시간을 반환
19
Logic
public IEnumerable<int> RegenerateEntry()
{
while (true)
{
var newNpc = _context.NewGameObject(ObjectType.Npc);
_context.AddGameObject(newNpc);
_context.BroadcastPacket(newNpc.ToSpawnPacket());
var newAi = new EachAi(this, newNpc);
_context.AddEntry(newAi.AiLogicEntry);
var nextInterval = _random.Next(interval) + base;
yield return nextInterval;
}
제어권이 호출자에게 넘어감
다음 호출 시 이 지점부터 수행
IEnumerator (Coroutine)
• IEnumerator의 MoveNext() 함수로 코드 실행
• Current로 yield return 결과 값 확인
20
Logic
var enumerator = RegenerateEntry().GetEnumerator();
while (enumerator.MoveNext())
{
Thread.Sleep(enumerator.Current);
}
IEnumerable로부터 IEnumerator를 가져옴
MoveNext()로 yield 사이 구간 코드 수행
yield return 반환 값을 얻음. 대기 시간만큼 쉼
여러 IEnumerator를 관리하고 Thread.Sleep()을 보다 작은 단위로 수행
LogicEngine (Coroutine)
21
Logic
public void EntryLoop()
{
var prev = DateTime.Now;
while (true)
{
var now = DateTime.Now;
var delta = (now - prev).Milliseconds;
foreach (var newOne in _newLogicEntries)
{
var newEntry = new LogicEntry
{
Enumerator = newOne().GetEnumerator(),
SleepTime = 0
};
_logicEntries.Add(newEntry);
}
_newLogicEntries.Clear();
새로 추가된 Entry로부터
IEnumerator객체를 생성
LogicEngine (Coroutine)
22
Logic
var removals = new List<LogicEntry>();
foreach (var each in _logicEntries)
{
each.SleepTime -= delta;
if (each.SleepTime >= 0)
continue;
if (!each.Enumerator.MoveNext())
removals.Add(each);
else each.SleepTime = each.Enumerator.Current;
}
_logicEntries.RemoveAll(removals.Contains);
prev = now;
const int logicInterval = 16;
Thread.Sleep(logicInterval);
}
}
수행 가능한 IEnumerator 집합
수행할 시간이 된 로직을 찾아서 실행
로직 함수가 return되어 완료되면 MoveNext()가 fasle를 반환
수행이 완료된 로직 삭제
Summary
• IEnumerable과 yield return의 조합으로 coroutine 구현
• 간단한 coroutine이지만 많은 boiler plate 코드 작성 회피 가능
• yield return으로 많은 정보를 전달하여 다양한 활용 (Unity3D Engine)
• 여러 Thread가 LogicEngine을 수행하여 Entry 수행 분산 가능
(여러 Thread가 수행할 경우 Lfe 등 객체별 수행 동기화 고려가 필요함)
• Script를 C#으로 작성 시 도움이 될 듯(?)
(c# script + roslyn + linqpad + nuget)
23
Logic
Summary
• async, await을 사용한 동기적 Network(IO) 프로그래밍
• dynamic을 사용한 Runtime type dispatch
• Reflection, Attribute를 사용한 boiler plate 코드 줄이기
• Attribute로 metadata를 코드로 기록하여 유지 보수 비용 줄이기
• coroutine을 사용한 동기적 로직 프로그래밍
24
보다 적은, 그리고 직관적인(동기적) 코딩으로 유지 보수 비용 줄이기
높은 표현력
성능 문제는 회로의 발전이 해결해 줄 것입니다 [...]
How much faster is C++ than C#?
25
C# may not be faster, but it makes
YOU/ME faster. That's the most
important measure for what I do. :)
http://stackoverflow.com/questions/138361/how-much-faster-is-c-than-c

More Related Content

PDF
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
PDF
Windows Registered I/O (RIO) vs IOCP
PDF
ニコニコ生放送の配信基盤改善
PDF
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
PDF
MMOG Server-Side 충돌 및 이동처리 설계와 구현
PPTX
StackStormを活用した運用自動化の実践
PDF
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
PDF
NDC12_Lockless게임서버설계와구현
Ndc2014 시즌 2 : 멀티쓰레드 프로그래밍이 왜 이리 힘드나요? (Lock-free에서 Transactional Memory까지)
Windows Registered I/O (RIO) vs IOCP
ニコニコ生放送の配信基盤改善
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
MMOG Server-Side 충돌 및 이동처리 설계와 구현
StackStormを活用した運用自動化の実践
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
NDC12_Lockless게임서버설계와구현

What's hot (20)

PDF
Twitter의 snowflake 소개 및 활용
PDF
게임서버프로그래밍 #1 - IOCP
PPTX
Kafka monitoring using Prometheus and Grafana
PDF
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
PPTX
C#을 사용한 빠른 툴 개발
PDF
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
PDF
How to build massive service for advance
PDF
いまさら聞けないパスワードの取り扱い方
PDF
[NDC07] 게임 개발에서의 클라이언트 보안 - 송창규
PDF
PostgreSQL Streaming Replication Cheatsheet
PDF
Multiplayer Game Sync Techniques through CAP theorem
PDF
중앙 서버 없는 게임 로직
PDF
LockFree Algorithm
PPTX
Common Performance Pitfalls in Odoo apps
PDF
Nodejs vatsal shah
PDF
Understanding the Single Thread Event Loop
PPTX
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
PDF
[오픈소스컨설팅] 스카우터 사용자 가이드 2020
PDF
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
PDF
The Internals of "Hello World" Program
Twitter의 snowflake 소개 및 활용
게임서버프로그래밍 #1 - IOCP
Kafka monitoring using Prometheus and Grafana
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
C#을 사용한 빠른 툴 개발
Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)
How to build massive service for advance
いまさら聞けないパスワードの取り扱い方
[NDC07] 게임 개발에서의 클라이언트 보안 - 송창규
PostgreSQL Streaming Replication Cheatsheet
Multiplayer Game Sync Techniques through CAP theorem
중앙 서버 없는 게임 로직
LockFree Algorithm
Common Performance Pitfalls in Odoo apps
Nodejs vatsal shah
Understanding the Single Thread Event Loop
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[오픈소스컨설팅] 스카우터 사용자 가이드 2020
인프콘 2022 - Rust 크로스 플랫폼 프로그래밍
The Internals of "Hello World" Program
Ad

Similar to C# Game Server (20)

PDF
.NET에서 비동기 프로그래밍 배우기
PPTX
Naver api for android
PPT
Gcd ppt
PPTX
Android xml parsing
PDF
android_thread
PDF
Javascript 조금 더 잘 알기
PDF
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
PDF
Ji 개발 리뷰 (신림프로그래머)
PDF
pgday-2023
PPTX
ECMAScript 6의 새로운 것들!
PPTX
5-4. html5 offline and storage
PPT
I phone 2 release
PPTX
TenforFlow Internals
PDF
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
PDF
Rx java intro
PDF
SpringCamp 2013 : About Jdk8
PDF
Tensorflow service & Machine Learning
PPTX
Nodejs, PhantomJS, casperJs, YSlow, expressjs
PDF
20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)
PPTX
Node.js and react
.NET에서 비동기 프로그래밍 배우기
Naver api for android
Gcd ppt
Android xml parsing
android_thread
Javascript 조금 더 잘 알기
[네이버오픈소스세미나] Pinpoint를 이용해서 서버리스 플랫폼 Apache Openwhisk 트레이싱하기 - 오승현
Ji 개발 리뷰 (신림프로그래머)
pgday-2023
ECMAScript 6의 새로운 것들!
5-4. html5 offline and storage
I phone 2 release
TenforFlow Internals
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
Rx java intro
SpringCamp 2013 : About Jdk8
Tensorflow service & Machine Learning
Nodejs, PhantomJS, casperJs, YSlow, expressjs
20140528 AWS Meister BlackBelt - Amazon Kinesis (Korean)
Node.js and react
Ad

More from lactrious (7)

PDF
Layered System prototype
PDF
Policy based Class Design
PDF
Preprocessor Programming
PDF
AWS GameServer Management
PPTX
Index Analysis
PPTX
Synchronizing concurrent threads
PPTX
omega design proposal
Layered System prototype
Policy based Class Design
Preprocessor Programming
AWS GameServer Management
Index Analysis
Synchronizing concurrent threads
omega design proposal

C# Game Server

  • 1. C# Server 만들기 2013. 06. 07. 최재영
  • 2. Why? • 빌드 속도 • 표현력 2 async await extension method linq Observable TPL DynamicObject Reflection Attribute IEnumerable
  • 4. Network • 빠른 패킷 처리를 위해 비동기 IO 사용 4 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IPv4); // preprocess socket var buffer = new byte[4096]; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, result => { var received = socket.EndReceive(result); // process packet }, null /* state */);
  • 5. TaskCompletionSource • C++ Future + C# Task Awaitable Future 5 Network static Task<int> ReceiveAsync(this Socket sock, byte[] buf, int off, int size) { var source = new TaskCompletionSource<int>(sock); sock.BeginReceive(buf, off, size, SocketFlags.None, state => { try { source.SetResult(socket.EndReceive(state)); } catch (Exception e) { source.SetException(e); } }, source); return source.Task; }
  • 6. async, await • Task.Result 비동기 대기(await), 그런 코드가 있는 함수(async) 6 Network static async Task<byte[]> ReceiveAsync(this Socket socket, int count) { var buffer = new byte[count]; var length = 0; do { var num = await ReceiveAsync(socket, buffer, length, count); if (num == 0) break; length += num; count -= num; } while (count > 0); if (length != buffer.Length) throw new IOException("packet is truncated."); return buffer; }
  • 7. async, await 7 Network async void ReceiveLoop(Socket socket) { while (true) { var lengthBytes = await socket.ReceiveAsync(sizeof (int)); var packetBytes = await socket.ReceiveAsync( BitConverter.ToInt32(lengthBytes, 0)); // process packet var packet = ReadPacket(packetBytes); _handlerMap[packet.GetType()](packet); } } await하는 지점에 아직 IO signal이 없다면, 해당 Task는 잠시 멈추고, 가용한 다른 Task를 찾아 수행함
  • 8. Listener (Server) • ClientSocket을 비동기로 Accept해서, • 각 Socket마다 비동기로 Packet을 대기해서 처리함 8 Network var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var localEndPoint = new IPEndPoint(IPAddress.Any, Port); listener.Bind(localEndPoint); listener.Listen(100); while (true) { var clientSocket = await listener.AcceptAsync(); ReceiveLoop(clientSocket); } async method
  • 9. Summary • Socket의 Async 계열 함수를 사용 (.NET 내부는 IOCP로 처리) • async, await은 TaskContinuation의 Syntax sugar • async, await Keyword로 Callback 없이 편하게 Network 코드 작성 • 그러면서도 Thread Pool에 의한 효율적으로 수행됨 (.NET Thread Pool도 내부에서 IOCP로 관리) 9 Network
  • 10. Datasheet • dynamic을 사용한 일반적인 Xml 읽기 • code generator를 사용 10 Xml 작성 Xml Model 구현 Xml Parser 구현 자동 생성 자동 생성 code generator DynamicObject XmlDefinition (자동 생성) general-loader
  • 11. DynamicObject • RuntimeType으로 동적으로 멤버 접근 가능 • 올바른 Type의 값을 미리 준비해야 하므로 XmlDefinition 필요 11 Datasheet public class XmlObject : DynamicObject { private readonly Dictionary<string, object> _attributes; private readonly Dictionary<string, IEnumerable<XmlObject>> _multipleChildren; private readonly Dictionary<string, XmlObject> _singleChildren; public override bool TryGetMember(GetMemberBinder binder, out object result) { return TryGetValue(_attributes, binder.Name, out result) || TryGetValue(_singleChildren, binder.Name, out result) || TryGetValue(_multipleChildren, binder.Name, out result); }
  • 12. DynamicObject • Attribute를 읽을 때 Type 변환을 미리 수행 • dynamic으로 접근하여 Model 없이 접근 가능 12 Datasheet _attributes = element.Attributes.OfType<XmlAttribute>() .ToDictionary(e => e.Name, e => defNode.SelectAttribute(e.Name) .ReadValue(e.Value)); <?xml version="1.0" encoding="utf-8" ?> <World> <Config port="40123"/> </World> World.Config@port : int dynamic world = XmlObject.Load("World.xml", _def); _listener.Port = world.Config.port;
  • 13. Summary • dynamic을 사용하여 코딩 시간 단축(Model, Parser 작성 불필요) • 오타로 인한 접근 위반은 Runtime에 확인 가능 • XmlDefinition이 필요함(자동 생성 가능) • 보다 빠른 속도를 원할 경우에는 Model, Parser를 Generate (IVsSingleFileGenerator) 13 Datasheet
  • 14. Database • Reflection과 Attribute 사용으로 일반적인 Bind 구현 • scheme 작업이 불필요할 경우 model 작성만으로 모든 구현 해결 가능 14 Scheme 작성 DataModel 작성 Bind 구현 nosql or generator Reflection
  • 15. Reflection • Runtime에 model의 type정보로 scheme를 구축 • 각 데이터의 Serialize/Deserialize 구현 필요 (String과 object 상호 변환) • 모든 Model 객체를 Xml로 변환 15 Database new XElement("Objects", _gameObjects.Values.Select( obj => new XElement("Object", obj.GetType().GetProperties() .Where(e => e.CanRead && e.CanWrite) .Select(e => new XAttribute(e.Name, SerializeValue(e.PropertyType, e.GetValue(obj, null)) ))))); 모든 Property에 대해 출력 출력할 때에는 string으로, 읽을 때에는 다시 object로
  • 16. Attribute • Runtime에 접근 가능한 metadata를 코드에 주입 16 Database [CommandHandler("npc", "새로운 Npc를 생성합니다")] internal bool SpawnNpc(Entity admin, [CommandArgument("Npc의 이름")] string npcName, [CommandArgument("Npc의 X 위치", 0)] double newX, [CommandArgument("Npc의 Y 위치", 0)] double newY) { if (!admin.Has<Pos>()) return false; var npc = EntityManager.Instance.Create(EntityTemplate.Ids.Npc); npc.Get<Motion>().Dir = admin.Get<Motion>().Dir; npc.Get<Nameplate>().Name = npcName; npc.Get<Pos>().Assign(new Pos {X = newX, Y = newY}); 명령어와 설명을 코드에 기록 인자 설명과 기본 값, type을 코드에 기록
  • 17. Summary • model 객체의 type 정보를 최대한 사용 • Attribute를 부여하여 가능한 많은 정보를 코드에 주입 (DSL, 문서, 주석 등 외부 정보는 추가 유지 보수가 필요함) • Dirty나 Lazy를 사용하여 최적화 가능 • 역시 보다 빠른 속도를 원할 경우에는 Code Generate를 사용 (partial class를 사용하여 사용자 코드와 혼합 가능) 17 Database
  • 18. Logic • yield return을 사용하여 State Machine 제거 • context 유지를 위한 별도 코딩이 필요 없음 18 행동 A 행동 B 행동 C 1초 뒤 3초 뒤 5초 뒤 Logic Engine Logic #1 Logic #2
  • 19. IEnumerable (Coroutine) • IEnumerable을 반환 type으로 설정하여 yield return 사용 • 다음 로직 수행까지의 대기 시간을 반환 19 Logic public IEnumerable<int> RegenerateEntry() { while (true) { var newNpc = _context.NewGameObject(ObjectType.Npc); _context.AddGameObject(newNpc); _context.BroadcastPacket(newNpc.ToSpawnPacket()); var newAi = new EachAi(this, newNpc); _context.AddEntry(newAi.AiLogicEntry); var nextInterval = _random.Next(interval) + base; yield return nextInterval; } 제어권이 호출자에게 넘어감 다음 호출 시 이 지점부터 수행
  • 20. IEnumerator (Coroutine) • IEnumerator의 MoveNext() 함수로 코드 실행 • Current로 yield return 결과 값 확인 20 Logic var enumerator = RegenerateEntry().GetEnumerator(); while (enumerator.MoveNext()) { Thread.Sleep(enumerator.Current); } IEnumerable로부터 IEnumerator를 가져옴 MoveNext()로 yield 사이 구간 코드 수행 yield return 반환 값을 얻음. 대기 시간만큼 쉼 여러 IEnumerator를 관리하고 Thread.Sleep()을 보다 작은 단위로 수행
  • 21. LogicEngine (Coroutine) 21 Logic public void EntryLoop() { var prev = DateTime.Now; while (true) { var now = DateTime.Now; var delta = (now - prev).Milliseconds; foreach (var newOne in _newLogicEntries) { var newEntry = new LogicEntry { Enumerator = newOne().GetEnumerator(), SleepTime = 0 }; _logicEntries.Add(newEntry); } _newLogicEntries.Clear(); 새로 추가된 Entry로부터 IEnumerator객체를 생성
  • 22. LogicEngine (Coroutine) 22 Logic var removals = new List<LogicEntry>(); foreach (var each in _logicEntries) { each.SleepTime -= delta; if (each.SleepTime >= 0) continue; if (!each.Enumerator.MoveNext()) removals.Add(each); else each.SleepTime = each.Enumerator.Current; } _logicEntries.RemoveAll(removals.Contains); prev = now; const int logicInterval = 16; Thread.Sleep(logicInterval); } } 수행 가능한 IEnumerator 집합 수행할 시간이 된 로직을 찾아서 실행 로직 함수가 return되어 완료되면 MoveNext()가 fasle를 반환 수행이 완료된 로직 삭제
  • 23. Summary • IEnumerable과 yield return의 조합으로 coroutine 구현 • 간단한 coroutine이지만 많은 boiler plate 코드 작성 회피 가능 • yield return으로 많은 정보를 전달하여 다양한 활용 (Unity3D Engine) • 여러 Thread가 LogicEngine을 수행하여 Entry 수행 분산 가능 (여러 Thread가 수행할 경우 Lfe 등 객체별 수행 동기화 고려가 필요함) • Script를 C#으로 작성 시 도움이 될 듯(?) (c# script + roslyn + linqpad + nuget) 23 Logic
  • 24. Summary • async, await을 사용한 동기적 Network(IO) 프로그래밍 • dynamic을 사용한 Runtime type dispatch • Reflection, Attribute를 사용한 boiler plate 코드 줄이기 • Attribute로 metadata를 코드로 기록하여 유지 보수 비용 줄이기 • coroutine을 사용한 동기적 로직 프로그래밍 24 보다 적은, 그리고 직관적인(동기적) 코딩으로 유지 보수 비용 줄이기 높은 표현력 성능 문제는 회로의 발전이 해결해 줄 것입니다 [...]
  • 25. How much faster is C++ than C#? 25 C# may not be faster, but it makes YOU/ME faster. That's the most important measure for what I do. :) http://stackoverflow.com/questions/138361/how-much-faster-is-c-than-c