성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>System.Net.Sockets.NetworkStream이 Thread-safe할까?</h1> <p> socket 관련 thread-safe의 마지막 글입니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Net.Sockets.Socket이 Thread-safe할까? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1469'>http://www.sysnet.pe.kr/2/0/1469</a> Win32 socket이 Thread-safe할까? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1473'>http://www.sysnet.pe.kr/2/0/1473</a> </pre> <br /> 이번에는 Socket I/O를 Stream으로 감싼 System.Net.Sockets.NetworkStream 차례입니다. 아래의 문서에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > NetworkStream Class ; <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream'>https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream</a> </pre> <br /> NetworkStream 역시 일반 BCL과 마찬가지의 thread-safety 정책이 걸려 있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Thread Safety: Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.<br /> </div><br /> <br /> 그래서 지난번 글과 동일하게 테스트를 해보았는데, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void Main(string[] args) { Thread.Sleep(2000); string body = string.Empty; StringBuilder sb = new StringBuilder(); Array.ForEach(Enumerable.Range(0, 10).ToArray(), (elem) => body += elem.ToString()); for (int i = 0; i < 10000; i++) { sb.Append(body); } body = sb.ToString(); int loopCount = 10000; int dot = (loopCount / (80 * 25)); List<CommonPacket> packets = new List<CommonPacket>(); using (TcpClient socket = new TcpClient("192.168.0.6", 11200)) { // IPAddress target = IPAddress.Loopback; IPAddress target = IPAddress.Parse("192.168.0.6"); EndPoint serverEP = new IPEndPoint(target, 11200); NetworkStream ns = socket.GetStream(); int instanceId = 0; byte[] instanceIdBuf = BitConverter.GetBytes(instanceId); ns.Write(instanceIdBuf, 0, 4); for (int i = 0; i < loopCount; i++) { CommonPacket packet = new CommonPacket(i); packet.AddData(body); packets.Add(packet); } List<Thread> threads = new List<Thread>(); ThreadParam threadParam = new ThreadParam(); threadParam.Packets = packets; threadParam.NetStream = ns; threadParam.Dot = dot; <span style='color: blue; font-weight: bold'> for (int i = 0; i < 20; i++) { Thread aThread = new Thread(sendBufferThread); aThread.IsBackground = true; aThread.Start(threadParam); threads.Add(aThread); }</span> foreach (var item in threads) { item.Join(); } socket.Close(); Console.WriteLine("TCP Client socket: Closed"); Console.WriteLine(loopCount + " times: data sent"); Console.WriteLine((body.Length / 1024) + "KB / packet"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Press any key to exit"); Console.ReadLine(); } } private static void sendBufferThread(object obj) { ThreadParam threadParam = (ThreadParam)obj; while (true) { CommonPacket packet = null; lock (threadParam.Packets) { if (threadParam.Packets.Count == 0) { break; } packet = threadParam.Packets[0]; threadParam.Packets.RemoveAt(0); threadParam.Sent++; if (threadParam.Sent % threadParam.Dot == 0) { Console.Write("."); } } byte[] dataBuf = packet.GetBuffer(); MustSendBuffer(threadParam.NetStream, dataBuf, dataBuf.Length); } } private static bool MustSendBuffer(NetworkStream ns, byte[] dataBuf, int mustSend) { <span style='color: blue; font-weight: bold'>ns.Write(dataBuf, 0, mustSend);</span> return true; } </pre> <br /> 결과는 thread-safe했습니다. 이상하군요. 그런대로 충돌이 날 만한데요. 이에 대해서는 NetworkStream.Write 메서드를 .NET Reflector를 통해 보면 짐작할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public override void Write(byte[] buffer, int offset, int size) { bool canWrite = <span style='color: blue; font-weight: bold'>this.CanWrite</span>; if (<span style='color: blue; font-weight: bold'>this.m_CleanedUp</span>) { throw new ObjectDisposedException(base.GetType().FullName); } if (!canWrite) { throw new InvalidOperationException(SR.GetString("net_readonlystream")); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if ((offset < 0) || (offset > buffer.Length)) { throw new ArgumentOutOfRangeException("offset"); } if ((size < 0) || (size > (buffer.Length - offset))) { throw new ArgumentOutOfRangeException("size"); } <span style='color: blue; font-weight: bold'>Socket streamSocket = this.m_StreamSocket</span>; if (streamSocket == null) { throw new IOException(SR.GetString("net_io_writefailure", new object[] { SR.GetString("net_io_connectionclosed") })); } try { streamSocket.Send(buffer, offset, size, SocketFlags.None); } catch (Exception exception) { if (((exception is ThreadAbortException) || (exception is StackOverflowException)) || (exception is OutOfMemoryException)) { throw; } throw new IOException(SR.GetString("net_io_writefailure", new object[] { exception.Message }), exception); } } </pre> <br /> 보시는 바와 같이 Write(및 Read) 메서드 안에서 인스턴스 멤버에 대한 조작을 하는 코드가 없습니다. 하부 단의 "Socket streamSocket" 멤버는 Socket 자체가 thread-safe하기 때문에 상관없고!<br /> <br /> 물론 NetworkStream 타입의 멤버가 thread-safe하지 않다고 되어 있으므로 향후 내부적으로 인스턴스 멤버를 조작하는 코드가 추가되는 경우 오동작을 할 여지가 있습니다. 따라서, 성능에 크게 영향이 없다면 NetworkStream을 사용하는 경우에는 lock을 적절하게 걸어주는 것이 권장됩니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=823&boardid=331301885'>첨부파일은 위의 동작을 테스트한 코드</a>를 담고 있습니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5016
(왼쪽의 숫자를 입력해야 합니다.)