C#은 동시성(Concurrency) 구현을 쉽게 할 수 있는데 이번 글에서 여러 개의 비동기 함수를 병렬처리 형태로 구현해 보는 예제를 살펴볼 것이다. Concurrency 방식과 Parallel 방식의 비교1는 이전 글인 Concurrency in C# Example 에서 살펴보았다.
아래의 예제처럼 각 1초, 2초, 3초를 소요하는 비동기 방식의 함수가 3개 있다고 가정하고 IProgress 또는 리턴값을 처리한다고 하면 총 6초가량이 걸리게 된다.
async Task Main()
{
string returnValue1 = await ProgressA(); //1초
string returnValue2 = await ProgressB(); //2초
string returnValue3 = await ProgressC(); //3초 : 총 6초
}
위의 예제를 아래처럼 Concurrency를 사용하면 총 3초가 소요되는 병렬처리가 가능하다2.
닷넷은 Win32 API와 같은 Native DLL을 using System.Runtime.InteropServices
네임스페이스를 통해 호출하는 방법을 제공한다. C++1, Delphi2에서 작성한 DLL 파일을 호출하는 방법은 이미 살펴보았다.
이번 글에서 살펴볼 것은 .NET 7에서 제공하는 Native AOT 컴파일을 사용하여 C#으로 Native DLL을 만들고 이를 불러와 사용하는 간단한 예제이다3. 아래의 소스처럼 ‘NativeLibrary’ 이름으로 프로젝트(DLL, .NET7)를 만든다. 특히 UnmanagedCallersOnly
는 바로 해당 함수를 export 하는 용도로 사용할 수 있게 해준다.
using System.Runtime.InteropServices;
namespace NativeLibrary;
public class Class1
{
[UnmanagedCallersOnly(EntryPoint = "add")]
public static int Add(int a, int b)
{
return a + b;
}
[UnmanagedCallersOnly(EntryPoint = "sumstring")]
public static nint SumString(nint first, nint second)
{
string? my1String = Marshal.PtrToStringAuto(first);
string? my2String = Marshal.PtrToStringAuto(second);
string sum = my1String + my2String;
nint sumPointer = Marshal.StringToHGlobalAuto(sum);
return sumPointer;
}
}
nint
타입은 Native int를 말하는데 플랫폼(32/64bit)에 따라 다른 크기를 갖는 정수이다. 위의 소스를 아래의 프로젝트 컴파일 옵션을 사용하여 dll 파일을 생성한다. 자세한 내용은 Building Native Libraries with NativeAOT를 참고한다.
Rust 프로젝트는 터미널에서 cargo
명령어를 통해서 생성 할 수 있다. 프로젝트 hello
를 생성한다고 가정할 때 임의의 디렉터리에서 cargo new hello --bin
을 실행하면 hello
디렉터리가 만들어지고 이 안에 기본 뼈대가 생성된다. 또는 직접 hello
디렉터리를 만들고 이 안에서 cargo init
명령어를 실행해도 똑같은 결과를 볼 수 있다.
hello\
src\
main.rs
Cargo.toml
main()
함수가 존재하는 기본 메인 파일[dependencies]
hello
디렉터리 안에서 cargo check
, cargo build
, cargo run
을 실행하면 결과를 볼 수 있는데 check은 문법 확인, build는 결과 생성, run은 결과를 실행한다. 바로 실행하고 싶다면 cargo run
을 실행한다.