잘못된 SqlConnection

// 코드 1

using (var connection = new SqlConnection("접속문자열"))

{

    connection.Open();

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}


<코드 1>을 실행하면 크래쉬가 발생

이유는 간단 SqlConnection가 스레드 세이프 하지 않기 때문.


위의 코드를 올바르게 수정하려면 아래와 같이 변경.

// 코드 2

Parallel.For(1, 1000, x =>

{

    using (var connection = new SqlConnection("접속문자열"))

    {

        connection.Open();

        var _ = connection.Query<DateTime>("select current_timestamp").First(); // Dapper

    }

});



<코드 2>는 안전하지만 아마 위 방식은 원하는 방식이 아닐 것이다.



ThreadLocal

위의 문제를 ThreadLocal을 사용하여 해결해 보자


// 코드 3

using (var connection = new ThreadLocal<SqlConnection>(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }))

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}



성능 테스트를 해보면 싱글 스레드를 사용하면 16초, <코드 2>는 5초, <코드 3>은 2초



<코드 3>은 안전한가?

<코드 3>은 위험한 코드.

이유는 연결한 후 Dispose 하지 않기 때문.

ThreadLocal의 Dispose는 어디까지는 ThreadLocal의 Dispose이기 때문에 그 안의 객체를 Dispose 해주지 않는다.


이 문제는 trackAllValues 라는 옵션을 사용하여 간단하게 해결 가능!



// 코드 4

using (var connection = new ThreadLocal<SqlConnection>(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }

    , trackAllValues: true)) // ThreadLocalの.Values 프로퍼티의 참조를 유효화 한다

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });


    // 생성된 모든 Connection을 일괄적으로 Dispose

    foreach (var item in connection.Values.OfType<IDisposable>()) item.Dispose();

}



단 trackAllValues는 닷넷프레임워크 4.5 부터 사용 가능




trackAllValues 랩퍼 클래스 사용


public static class DisposableThreadLocal

{

    public static DisposableThreadLocal<T> Create<T>(Func<T> valueFactory)

        where T : IDisposable

    {

        return new DisposableThreadLocal<T>(valueFactory);

    }

}


public class DisposableThreadLocal<T> : ThreadLocal<T>

    where T : IDisposable

{

    public DisposableThreadLocal(Func<T> valueFactory)

        : base(valueFactory, trackAllValues: true)

    {

    }


    protected override void Dispose(bool disposing)

    {

        var exceptions = new List<Exception>();


        foreach (var item in this.Values.OfType<IDisposable>())

        {

            try

            {

                item.Dispose();

            }

            catch (Exception e)

            {

                exceptions.Add(e);

            }

        }


        base.Dispose(disposing);


        if (exceptions.Any()) throw new AggregateException(exceptions);

    }

}



// 사용

using (var connection = DisposableThreadLocal.Create(() => { var conn = new SqlConnection("접속문자열"); conn.Open(); return conn; }))

{

    Parallel.For(1, 1000, x =>

    {

        var _ = connection.Value.Query<DateTime>("select current_timestamp").First(); // Dapper

    });

}





출처: http://neue.cc/2013/03/09_400.html 

저작자 표시
신고
by 흥배 2016.11.04 08:00

C#은 배열의 오버런을 방지하기 위해 자동으로 for 문으로 배열을 순회할 때 자동으로 경계 조사를 한다.

문제는 배열의 크기를 확실하게 알고 배열 경계를 넘지 않도록 프로그래머 주의를 해도 컴파일러가 경계 조사 코드를 만들어 버려서 성능 측면에서 손해를 볼 수 있다.

 

배열의 경계 조사를 하지 않고 싶다면 for 문에서 조건 검사를 배열의 Length 멤버를 사용한다

 

static void Test_SimpleAscend(int[] a) {

           for (int i = 0; i < a.Length; i++)

                     a[i] = i;  // We get this.

}

 



좀 더 자세한 내용은 아래 링크를 참조

https://blogs.msdn.microsoft.com/clrcodegeneration/2009/08/13/array-bounds-check-elimination-in-the-clr/

 

저작자 표시
신고
by 흥배 2016.08.08 08:00

. NET 4.6에서 GC.TryStartNoGCRegion ~ GC.EndNoGCRegion 라는 것이 추가 되었다.

GC.TryStartNoGCRegion에서 GC.EndNoGCRegion까지 자동 GC를 억제할 수 있다.

 

 

처리 중 GC가 일어날 경우

아래 코드는 1MB의 바이트 배열을 생성하고 GC 회수 대상이 될 수 있는 구성이다.

using System;

using System.Runtime;

using System.Threading;

 

namespace TestSpace {

    class Program {

        static void Main( string[] args ) {

            for( int i = 0; i < 30; i++ ) {

                var n = create();

                Thread.Sleep( 100 );

                GC.KeepAlive( n );

 

                Console.WriteLine( $"GC:{GC.CollectionCount( 0 ) } {GC.GetTotalMemory( false ):#,##byte} " );

            }

            Console.ReadLine();

 

        }

 

        public static WeakReference<byte[]> create() {

            var weak = new WeakReference<byte[]>( new byte[1048576] );

            return weak;

        }

 

    }

}

 

동작 환경에 따라서 차이가 있겠지만 몇 번 GC가 발생하는지 확인할 수 있다.

 

 

GC.TryStartNoGCRegion ~ GC.EndNoGCRegion 로 둘러싸 본다

속도가 중시되는 경우 GC 발생이 병목이 될 수 가능성이 있다.

 

GC.TryStartNoGCRegion에 메모리 사이즈(바이트 단위)를 지정한다.

이곳에서 지정한 사이즈가 소비될 때까지 GC가 억제되게 된다.

 

예를 들어 GC.TryStartNoGCRegion(15728640)라고 지정하면 15MB 소비할 때까지 GC를 억제하게 된다.

또한 지정 가능한 최대 크기는 실행 환경에 의해서 결정된다.

상세는 Fundamentals of Garbage Collection을 참조.

https://msdn.microsoft.com/en-us/library/ee787088(v=vs.110).aspx

 

일반적인 클라이언트 PC 32비트 환경에서는 16MB, 64비트 환경에서는 256MB 이다.

아래의 예는 32비트 환경을 예상한 것이다.

using System;

using System.Runtime;

using System.Threading;

 

namespace TestSpace {

    class Program {

        static void Main( string[] args ) {

 

            if( GC.TryStartNoGCRegion( 15728640 ) ) {

                for( int i = 0; i < 30; i++ ) {

                    var n = create();

                    Thread.Sleep( 100 );

                    GC.KeepAlive( n );

 

                    Console.WriteLine( $"GC:{GC.CollectionCount( 0 ) } {GC.GetTotalMemory( false ):#,##byte} " );

                }

 

                if( GCSettings.LatencyMode == GCLatencyMode.NoGCRegion )

                    GC.EndNoGCRegion();

            }

            Console.ReadLine();

 

        }

 

        public static WeakReference<byte[]> create() {

            var weak = new WeakReference<byte[]>( new byte[1048576] );

            return weak;

        }

 

    }

 

}

 

GC.TryStartNoGCRegion 에서 지정한 15MB까지 GC가 이뤄지지 않는 것을 확인할 수 있다.

그러나 15MB를 초과 한 시점에서 자동적으로 GC가 발동 하는 것도 확인할 수 있다.

 

 

주의 사항

GC.TryStartNoGCRegion을 호출해서 새롭게 할당된 메모리가 지정 크기까지 도달할 때까지 GC를 억제하는 것이다.

이미 할당된 메모리 사이즈는 무의미하다.

 

GC.TryStartNoGCRegion을 다중으로 호출 할 수는 없다.

GC.TryStartNoGCRegion에 성공하면 GCSettings.LatencyMode값이 GCLatencyMode.NoGCRegion로 변경된다.

거꾸로 말하면, GCSettings.LatencyMode == GCLatencyMode.NoGCRegion 이라면 GC 억제 중이라고 판단할 수 있다.

 

GC.EndNoGCRegion() GCSettings.LatencyMode GCLatencyMode.NoGCRegion인 경우에만 실행 가능하다.

GC.EndNoGCRegion()를 호출하지 않아도 자동으로 해제되는 경우가 있다.

GC.TryStartNoGCRegion에서 지정한 사이즈에 달했다

GC.Collect등이 호출 되어 수동에 의한 GC가 발생했다

GC.GetTotalMemory(true)가 실행되었다

 

이런 경우 그 시점에서 GC의 억제 상태가 해제된다.

 

 

MSDN: GC.TryStartNoGCRegion

https://msdn.microsoft.com/en-us/library/system.gc.trystartnogcregion(v=vs.110).aspx

 

동시에 복수의 GC.TryStartNoGCRegion을 호출한 경우 InvalidOperationException이 발생하므로, 멀티 쓰레드에서 GC.TryStartNoGCRegion~GC.EndNoGCRegion을 할 때는 반드시 동기화 해야 한다.

 

출처: http://qiita.com/Temarin_PITA/items/749ad661fd13d7402794

 

저작자 표시
신고
by 흥배 2016.08.01 08:00

현재 내용으로 봐서는 2017 2분기쯤 되면 꽤 쓸만하겠네요^^;

링크: https://blogs.msdn.microsoft.com/dotnet/2016/07/15/net-core-roadmap/

 

 

8월말쯤에 1.0.1 나올 예정.

 

올해 말 혹은 내년 3월까지의 예정.

 

 

.NET Core Tooling

- .csproj/MSBuild project system 지원

- 프레임워크 관리용 명령어 추가

 

 

언어 관련. C# 7, VB 15 추가 언어 사양

- tuple, 패턴 매칭

- Value Task, Ref return, Throw expression, Binary Literals, Digit Separators

- Out vars, Local Function

 

 

ASP.NET

- Web Socket

- URL Rewriting Middleware

- Azure 관련 강화

- Service Fabric support via WebListener based server

- MVC & DI Startup Time Improvements

- 프리뷰

- SignalR

- View Pages (Views without MVC Controllers)

 

 

.NET Core Runtime

- ARM 32/64

- .NET Core Runtime

 

 

Entity Framework Core

- Azure

- Transient fault handling (resiliency)

- Mapping

- Custom type conversions

- Complex types (value objects)

- Entity entry APIs

- Update pipeline

- CUD stored procedures

- Better batching (TVPs)

- Ambient transactions

- Query

- Stability, performance.

- Migrations

- Seed data

- Stability

- Reverse engineer

- Pluralization

- VS item template (UX)

 

 

Q1 2017 / Q2 2017

- .NET Standard 2.0

 

 

출처: https://opcdiary.net/?p=32475

 

저작자 표시
신고
by 흥배 2016.07.26 10:05

예정대로 5월 중순에 릴리스 되었다.

이번에 출시된 것은 다음과 같다.

.NET Core RC2

ASP.NET Core RC2

.NET Core SDK Preview 1

 

이 밖에 Azure에 대한 배포 도구, RHEL yum 인스톨러를 제공할 예정.

 

예고대로 런타임은 RC2 툴은 Preview가 이다.

지원되는 개발 환경은 다음과 같다.

Visual Studio 2015 Update2

Visual Studio Code(C# Extension)

Omnisharp

 

지원되는 플랫폼

Red Hat Enterprise Linux 7.2

Centos 7.1

Debian 8.2(8.2, 8.3, 8.4)+

Ubuntu 14.04(16.04 support is coming at RTM)

OS X 10.11

Windows 7+/Server 2012 R2+

Windows Nano Server TP5

 

정식으로 Debian, Cetos가 지원되었다.

툴에 대해서는 CLI 명령은 DNX 명령(dnx, dnvm, dnu)이 사라지고 dotnet 명령으로 바뀌었다.

dotnet 명령은 프로젝트의 작성, 패키지 관리, 빌드, 발행, 실행 테스트 실행 패키지 작성 인터페이스로 된다.

 

CLI툴 문서: .NET Core SDK

설치는 여기다. https://www.microsoft.com/net/core

 

 

출처: https://opcdiary.net/?p=32027

원 출처: https://blogs.msdn.microsoft.com/dotnet/2016/05/16/announcing-net-core-rc2/

저작자 표시
신고
by 흥배 2016.05.17 10:03

주요 일정

.NET Core ASP.NET Core 1.0 RC2의 런타임과 라이브러리는 5월 중순에 제공 개시 예정.

툴 버전은 Preview 1로 이 릴리스에 번들 된다.

 

.NET Core ASP.NET Core 1.0 RTM(릴리스)의 런타임과 라이브러리는 6 월말까지 제공 개시 예정.

툴 버전은 Preview 2로 이 릴리스에 번들 된다.

 

툴에 대해서는 안정화될 때까지 변경을 거듭, Visual Studio"15"와 함께 RTM 버전을 출시할 예정.

 

 

그 동안의 경위

모듈형, 크로스 플랫폼, 높은 퍼포먼스의 새로운 ASP.NET을 개발하기 위해서 2년 반 전에 ASP.NET 팀이 발족되었다.새로운 ASP.NET 버전은 클라우드에 초점을 맞춘 최신 워크 로드(Web 사이트, 마이크로 서비스 등)에 최적화된 새 DNX(.NET Execution Environment) 위에 구축되었다. 그리고 지난해 11월에 이 버전의 RC1이 출시되었다.

 

ASP.NET Core 1.0 RC1 출시 후에 가장 중시한 것은 .NET Core를 더욱 확장하고 네이티브 콘솔 애플리케이션 개발도 지원하는 것이었다. 그래서 한 것이 .NET 콘솔, 클래스 라이브러리, 서버 애플리케이션의 개발에 사용하는 툴 체인을 재구축하는 것이었다. 이 작업은 당초의 예상보다 지연되면서 2월에 예정했던 RC2/RTM의 릴리스는 철회할 수밖에 없었다.

 

 

프레임워크와 툴의 통합

올해 Xamarin 인수로 마이크로 소프트의 일부가 됨으로써 우리는 지금까지 이상으로 데스크 톱, 서버, 모바일 애플리케이션 간에 코드를 쉽게 공유할 수 있도록 해야겠다고 생각하였다.

 

그리고 복수의 .NET 어플리케이션 모델 간에 코드를 보다 쉽게 공유할 수 있도록 계획의 일부로서 Build에서 .NET Standard를 발표했다.

 

또 우리는 복수의 어플리케이션 모델에 걸친 프로젝트의 작업도 쉽게 할 필요가 있었다. 이 때문에 현재 진행 중인 것이 .xproj/project.json 프로젝트 시스템과 .csproj 프로젝트 시스템의 기능을 MSBuild 베이스의 하나로 프로젝트 시스템에 통합하는 것이다. 이행은 자동으로 이뤄지기 때문에 기존 프로젝트에 변경을 가할 필요는 없다. 이 작업은 Visual Studio 15 출시 일정 기간에 실시할 예정이다.

 

 

툴이 "프리뷰" 버전인 이유

.NET Core는 아래 2가지 주요 컴퍼넌트로 구성되어 있다.

런 타임 라이브러리: CLR, 라이브러리, 컴파일러 등

: .NET Core 프로젝트의 작업에 사용하는 NET Core 명령 라인 툴, Visual Studio, Visual Studio Code에서 지원되는 모든 도구

 

이번 .NET Core의 컴포넌트 마다 릴리즈 버전을 나누기로 했다. 이로써 기다리고 있는 분들은 .NET Core 1.0 RC2를 안심하고 이용하면서 한편으로는 마이크로 소프트는 툴 계획을 진행할 수 있다.

.NET Core 1.0 RC2 런타임은 정식 Release Candidate판이다. 신뢰성과 안정성이 확보되어 있어서(치명적인 문제가 발생하지 않는 한)RTM 판까지 변경되지 않는다. Go-Live라이선스가 부여되기 때문에 마이크로 소프트에서 정식 지원을 받을 수 있다.

새로운 명령 라인 툴이나 Visual Studio Visual Studio Code의 플러그 인 등 .NET Core ASP.NET Core를 지원하는 툴은 RC 판에는 아직 도달하고 있지 않고 안정화될 때까지 변경된다. 그래서 이 툴은 Preview 1으로서 출시된다.

 

출처: https://blogs.msdn.microsoft.com/dotnet/2016/05/06/net-core-rc2-improvements-schedule-and-roadmap/

 

 

저작자 표시
신고
by 흥배 2016.05.17 09:57

C#은 공용체를 지원하지 않지만 StructLayout(LayoutKind.Explicit) 과 FieldOffset을 사용해서 공용체를 구현할 수 있다.

 

using System;
using System.Runtime.InteropServices;

public class UnionTest
{
  public static void Main()
  {
     var bytes = new TBytes();
     bytes.Word = 0x12345678;
     Console.WriteLine("{0:x}", bytes.Byte0);  // 78
     Console.WriteLine("{0:x}", bytes.Byte1);  // 56
     Console.WriteLine("{0:x}", bytes.Byte2);  // 34
     Console.WriteLine("{0:x}", bytes.Byte3);  // 12
  }
}

[StructLayout(LayoutKind.Explicit)]
public struct TBytes
{
  [FieldOffset(0)]
  public byte Byte0;

  [FieldOffset(1)]
  public byte Byte1;

  [FieldOffset(2)]
  public byte Byte2;

  [FieldOffset(3)]
  public byte Byte3;

  [FieldOffset(0)]
  public uint Word;
}

 

출처: http://qiita.com/tadnakam/items/a47421c120b5f30954fb

저작자 표시
신고
by 흥배 2016.02.15 08:00

class aaa {
        static void bbb (int a ,params string[] strs) {
        }
    }

    static void Main () {
        int a = 1;
        int b = 2;
        bbb(a,"foo","bar","baz");
        bbb(b);
    }
}

 

 

출처: http://qiita.com/satotin/items/81e1e9c41181c9766de6

저작자 표시
신고
by 흥배 2016.02.11 08:00

linq, ArrayCopy, BlockCopy, marshal 중 BlockCopy가 제일 빠름


테스트 코드

const int COUNT = 1000000;

static void Main(string[] args)

{

var basearray = Enumerable.Range(0, 1024).Select(x => (byte) x).ToArray();


var sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = basearray.Skip(20).Take(20).ToArray();

}

sw.Stop();


Console.WriteLine($"linq {sw.ElapsedMilliseconds}ms");


sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Array.Copy(basearray, 20, buffer, 0, 20);

}

sw.Stop();


Console.WriteLine($"ArrayCopy {sw.ElapsedMilliseconds}ms");


sw = Stopwatch.StartNew();

for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Buffer.BlockCopy(basearray, 20, buffer, 0, 20);

}

sw.Stop();


Console.WriteLine($"BlockCopy {sw.ElapsedMilliseconds}ms");



sw = Stopwatch.StartNew();

var ptr = Marshal.AllocCoTaskMem(basearray.Length);

Marshal.Copy(basearray, 0, ptr, basearray.Length);

try

{


for (var i = 0; i < COUNT; i++)

{

var buffer = new byte[20];

Marshal.Copy(ptr + 20, buffer, 0, 20);

}

}

finally

{

Marshal.FreeCoTaskMem(ptr);

}

sw.Stop();


Console.WriteLine($"marshal {sw.ElapsedMilliseconds}ms");


Console.ReadKey();

}


결과

linq          1622ms

ArrayCopy   71ms

BlockCopy  55ms

marshal      62ms



출처: http://qiita.com/yu_ka1984/items/c97e0950486e3ecc5642 

저작자 표시
신고
by 흥배 2016.02.05 08:12

성능을 중요시 하는 프로그램을 만들 때(특히 Unity를 사용한 게임) GC를 조심해야 하므로 불필요한 new를 사용하지 않아야 한다.

그러니 일부 눈에 보이는 코드에는 new를 사용하지 않지만 내부에서 사용되는 경우가 있다.

 

1. 박싱화

var x = (object)10;

 

void Test(object o)

{

}

 

Test(10);

 

 

2. 제너릭을 사용할 때 Object를 사용하는 메소드 사용

bool IsSame<T>(T t1, T t2)

{

    return t1.Equals(t2);

}

위의 IsSame은 제너릭 함수라서 박싱화가 일어나지 않을 것이라고 생각하지만 Equals 메소드가 Object를 사용해서 박싱화가 일어난다.

public virtual bool Equals(Object obj);

 

이 문제를 회피하기 위해서는 아래처럼 사용해야 한다.

public interface IEquatable<T>

{

    bool Equals(T other);

}

 

bool IsSame<T>(T t1, T t2) where T : IEquatable<T>

{

    return t1.Equals(t2);

}

 

혹은

bool IsSame<T>(T t1, T t2)

{

    return EqualityComparer<T>.Default.Equals(t1, t2);

}

 

 

3. 람다식의 보이지 않는 new

static int DoubleStatic(int x)

{

    return x * 2;

}

 

int DoubleInstance(int x)

{

    return x * 2;

}

 

void Run()

{

    var two = int.Parse("2");

 

    Enumerable.Range(1, 1).Select(DoubleStatic);           // 1

    Enumerable.Range(1, 2).Select(DoubleInstance);         // 2

    Enumerable.Range(1, 3).Select(x => x * 2);             // 3

    Enumerable.Range(1, 4).Select(x => x * two);           // 4

    Enumerable.Range(1, 5).Select(x => DoubleStatic(x));   // 5

    Enumerable.Range(1, 6).Select(x => DoubleInstance(x)); // 6

}

 

패턴 1, 2는 메소드를 직접 넣은 경우로 델리게이트를 사용하므로 new를 사용한다.

패턴 3은 일반적으로 자주 사용하는 패턴으로 new를 사용하지 않는다.

패턴 4도 자주 사용되는 패턴으로 로컬 변수를 사용해서 클래스가 만들어지므로 new를 사용한다.

패턴 5static 메소드를 사용해서 첫 호출 이외에는 캐시된 것을 사용하므로 별 문제 되지 않는다.

패턴 6은 델리게이트를 사용하므로 new를 사용한다.

 

위의 코드의 안 보이는 코드를 표현하면 아래와 비슷해진다.

static Func<int, int> cacheA;

static Func<int, int> cacheB;

 

internal static int LambdaA(int x)

{

           return x * 2;

}

 

class Closure

{

    internal int two;

 

    internal int LambdaB(int x)

    {

        return x * two;

    }

}

 

internal static int LambdaC(int x)

{

           return DoubleStatic(x);

}

 

internal static int LambdaD(int x)

{

           return DoubleInstance(x);

}

 

void Run()

{

    var two = int.Parse("2");

 

    // 1 - Select(DoubleStatic)

    Enumerable.Range(1, 1).Select(new Func<int, int>(DoubleStatic));

 

    // 2 - Select(DoubleInstance)

    Enumerable.Range(1, 2).Select(new Func<int, int>(DoubleInstance));

 

    // 3 - Select(x => x * 2)

    if(cacheA != null)

    {

        cacheA = new Func<int, int>(LambdaA);

    }

    Enumerable.Range(1, 3).Select(cacheA);

 

    // 4 - Select(x => x * two)

    var closure = new Closure();

    closure.two = two;

    Enumerable.Range(1, 4).Select(new Func<int, int>(closure.LambdaB));

 

    // 5 - Select(x => DoubleStatic(x))

    if(cacheB != null)

    {

        cacheB = new Func<int, int>(LambdaC);

    }

    Enumerable.Range(1, 5).Select(cacheB);

 

    // 6 - Select(x => DoubleInstance(x))

    Enumerable.Range(1, 6).Select(new Func<int, int>(LambdaD));

}

 

 

 

출처: http://neue.cc/2016/01/06_525.html

 

저작자 표시
신고
by 흥배 2016.02.03 07:52
| 1 2 3 4 ··· 8 |