■ TaskCompletionSource<T> 클래스의 SetResult 메소드/Task 속성을 사용해 비동기 작업을 처리하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
using System; using System.Threading; using System.Threading.Tasks; namespace TestProject; /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 작업 처리하기 - ProcessOperationAsync() /// <summary> /// 작업 처리하기 /// </summary> /// <returns>작업 처리 결과 태스크</returns> private static Task<int> ProcessOperationAsync() { TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>(); Task.Run ( async () => { int mainThreadID = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 비동기 작업 시작"); await Task.Delay(3000); taskCompletionSource.SetResult(42); Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 비동기 작업 완료"); } ); return taskCompletionSource.Task; } #endregion #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> /// <returns>태스크</returns> private static async Task Main() { int mainThreadID = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 프로그램 시작"); Task<int> task = ProcessOperationAsync(); for(int i = 0; i < 50; i++) { Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 항목 {i}"); await Task.Delay(100); } int result = await task; Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 비동기 작업 결과 : {result}"); Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] 스레드 ID : {mainThreadID}, 프로그램 종료"); } #endregion } |
TestProject.zip
■ _ (discard) 연산자를 사용하는 방법을 보여준다. ※ await 키워드를 사용하지 않는다. ※ 대화 상자를 표시하는 비동기 작업을 시작하지만, 완료를 기다리지 않는다.
더 읽기
■ Task<T> 클래스의 ConfigureAwait 메소드를 사용해 await 이후 스레드 풀 스레드에서 실행하는 방법을 보여준다. ※ 기본적으로 async/await 패턴은 작업이 완료된 후 원래의
더 읽기
■ IAsyncOperation<T> 인터페이스의 AsTask 메소드를 사용해 Task<T> 객체를 구하는 방법을 보여준다. ▶ 예제 코드 (C#)
|
using System.Threading.Tasks; using Windows.Foundation; using Windows.Storage; StorageFolder storageFolder = await StorageFolder.GetFolderFromPathAsync(@"D:\"); IAsyncOperation<IStorageItem> storageItemAsyncOperation = storageFolder.TryGetItemAsync("sample.dat"); Task<IStorageItem> task = storageItemAsyncOperation.AsTask(); |
■ DispatcherQueueExtensions 클래스의 EnqueueAsync 확장 메소드를 사용해 DispatcherQueue 객체에서 특정 UI 스레드를 실행하는 방법을 보여준다. ▶ 예제 코드 (C#)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
using System.Threading.Tasks; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml.Controls; using CommunityToolkit.WinUI; DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread(); await dispatcherQueue.EnqueueAsync ( () => { } ); int value1 = await dispatcherQueue.EnqueueAsync ( () => { return 42; } ); await dispatcherQueue.EnqueueAsync ( async () => { await Task.Delay(100); } ); int someOtherValue = await dispatcherQueue.EnqueueAsync ( async () => { await Task.Delay(100); return 42; } ); |
■ DirectoryLoader 클래스의 생성자에서 use_multithreading 인자를 사용해 멀티스레딩을 설정하는 방법을 보여준다. ▶ main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
from langchain_community.document_loaders import DirectoryLoader directoryLoader = DirectoryLoader(".", glob = "*.md", use_multithreading = True) documentList = directoryLoader.load() for document in documentList: print(document) print() """ page_content='첫번째\n\n두번째\n\n세번째\n\n항목1\n\n항목 2\n항목 3\n항목 4' metadata={'source': 'list.md'} page_content='제목 1단계\n\n제목 2단계\n\n제목 3단계\n\n제목 4단계\n\n제목 5단계\n\n제목 6단계' metadata={'source': 'header.md'} """ |
▶ requirements.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
aiohttp==3.9.5 aiosignal==1.3.1 annotated-types==0.7.0 anyio==4.4.0 async-timeout==4.0.3 attrs==23.2.0 backoff==2.2.1 beautifulsoup4==4.12.3 certifi==2024.6.2 chardet==5.2.0 charset-normalizer==3.3.2 click==8.1.7 dataclasses-json==0.6.7 deepdiff==7.0.1 emoji==2.12.1 exceptiongroup==1.2.1 filetype==1.2.0 frozenlist==1.4.1 greenlet==3.0.3 h11==0.14.0 httpcore==1.0.5 httpx==0.27.0 idna==3.7 joblib==1.4.2 jsonpatch==1.33 jsonpath-python==1.0.6 jsonpointer==3.0.0 langchain==0.2.6 langchain-community==0.2.6 langchain-core==0.2.10 langchain-text-splitters==0.2.2 langdetect==1.0.9 langsmith==0.1.82 lxml==5.2.2 Markdown==3.6 marshmallow==3.21.3 multidict==6.0.5 mypy-extensions==1.0.0 nest-asyncio==1.6.0 nltk==3.8.1 numpy==1.26.4 ordered-set==4.1.0 orjson==3.10.5 packaging==24.1 pydantic==2.7.4 pydantic_core==2.18.4 pypdf==4.2.0 python-dateutil==2.9.0.post0 python-iso639==2024.4.27 python-magic==0.4.27 PyYAML==6.0.1 rapidfuzz==3.9.3 regex==2024.5.15 requests==2.32.3 requests-toolbelt==1.0.0 six==1.16.0 sniffio==1.3.1 soupsieve==2.5 SQLAlchemy==2.0.31 tabulate==0.9.0 tenacity==8.4.2 tqdm==4.66.4 typing-inspect==0.9.0 typing_extensions==4.12.2 unstructured==0.14.8 unstructured-client==0.23.7 urllib3==2.2.2 wrapt==1.16.0 yarl==1.9.4 |
※ pip install langchain-community unstructured[md]
더 읽기
■ apply 함수를 사용해 현재 이벤트 루프를 사용하는 방법을 보여준다. ※ 설계상 asyncio는 이벤트 루프가 중첩되는 것을 허용하지 않는다. 이는 실질적인 문제를
더 읽기
■ nest_asyncio 패키지를 설치하는 방법을 보여준다. 1. 명령 프롬프트를 실행한다. 2. 명령 프롬프트에서 아래 명령을 실행한다. ▶ 실행 명령
※ 쥬피터
더 읽기
■ Lock 클래스를 사용해 쓰레드에서 상호 배제 잠금을 설정하는 방법을 보여준다. ▶ 예제 코드 (PY)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
from threading import Lock from threading import Thread class Counter: def __init__(self): self.lock = Lock() self.count = 0 def increment(self, value): with self.lock: self.count += value def worker(loopCount, counter): for _ in range(loopCount): counter.increment(1) def runThreads(sourceFunction, loopCount, counter): threadList = [] for i in range(5): argumentTuple = (loopCount, counter) thread = Thread(target = sourceFunction, args = argumentTuple) threadList.append(thread) thread.start() for thread in threadList: thread.join() loopCount = 10 ** 5 counter = Counter() runThreads(worker, loopCount, counter) print("loopCount :", loopCount ) print("counter.count :", counter.count) """ loopCount : 100000 counter.count : 500000 """ |
■ Thread 클래스의 start/join 메소드를 사용하는 방법을 보여준다. ▶ 예제 코드 (PY)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
from threading import Lock from threading import Thread class Counter: def __init__(self): self.lock = Lock() self.count = 0 def increment(self, value): with self.lock: self.count += value def worker(loopCount, counter): for _ in range(loopCount): counter.increment(1) def runThreads(sourceFunction, loopCount, counter): threadList = [] for i in range(5): argumentTuple = (loopCount, counter) thread = Thread(target = sourceFunction, args = argumentTuple) threadList.append(thread) thread.start() for thread in threadList: thread.join() loopCount = 10 ** 5 counter = Counter() runThreads(worker, loopCount, counter) print("loopCount :", loopCount ) print("counter.count :", counter.count) """ loopCount : 100000 counter.count : 500000 """ |
■ gather 함수를 사용해 복수 비동기 함수 실행을 대기하는 방법을 보여준다. ▶ 예제 코드 (PY)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
import asyncio async def getFactorial(taskName, number): factorialValue = 1 for i in range(2, number + 1): print(f"{taskName} : {i} 팩토리얼 계산...") await asyncio.sleep(1) factorialValue *= i print(f"{taskName} : 팩토리얼({number}) = {factorialValue}") return factorialValue async def main(): factorialValueList = await asyncio.gather( getFactorial("작업 1", 10), getFactorial("작업 2", 10), getFactorial("작업 3", 10) ) #factorialValueList = await asyncio.gather(*[getFactorial("작업 1", 10), getFactorial("작업 2", 10), getFactorial("작업 3", 10)]) print(factorialValueList) asyncio.run(main()) """ 작업 1 : 2 팩토리얼 계산... 작업 2 : 2 팩토리얼 계산... 작업 3 : 2 팩토리얼 계산... 작업 1 : 3 팩토리얼 계산... 작업 2 : 3 팩토리얼 계산... 작업 3 : 3 팩토리얼 계산... 작업 1 : 4 팩토리얼 계산... 작업 2 : 4 팩토리얼 계산... 작업 3 : 4 팩토리얼 계산... 작업 1 : 5 팩토리얼 계산... 작업 2 : 5 팩토리얼 계산... 작업 3 : 5 팩토리얼 계산... 작업 1 : 6 팩토리얼 계산... 작업 2 : 6 팩토리얼 계산... 작업 3 : 6 팩토리얼 계산... 작업 1 : 7 팩토리얼 계산... 작업 2 : 7 팩토리얼 계산... 작업 3 : 7 팩토리얼 계산... 작업 1 : 8 팩토리얼 계산... 작업 2 : 8 팩토리얼 계산... 작업 3 : 8 팩토리얼 계산... 작업 1 : 9 팩토리얼 계산... 작업 2 : 9 팩토리얼 계산... 작업 3 : 9 팩토리얼 계산... 작업 1 : 10 팩토리얼 계산... 작업 2 : 10 팩토리얼 계산... 작업 3 : 10 팩토리얼 계산... 작업 1 : 팩토리얼(10) = 3628800 작업 2 : 팩토리얼(10) = 3628800 작업 3 : 팩토리얼(10) = 3628800 [3628800, 3628800, 3628800] """ |
■ run 함수를 사용해 비동기 함수를 동기 호출하는 방법을 보여준다. ▶ 예제 코드 (PY)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
import asyncio import time async def functionAsync1(): print("START functionAsync1") await asyncio.sleep(1) print("END functionAsync1") async def functionAsync2(): print("START functionAsync2") await asyncio.sleep(3) print("END functionAsync2") async def main(): startTme = time.time() await functionAsync1() await functionAsync2() endTime = time.time() print(f"경과 시간 : {endTime - startTme}") asyncio.run(main()) """ START functionAsync1 END functionAsync1 START functionAsync2 END functionAsync2 경과 시간 : 4.004331588745117 """ |
■ Duration 구조체의 from_millis 연관 함수를 사용해 밀리초 단위 Duration 객체를 구하는 방법을 보여준다. ▶ 예제 코드 (RS)
|
use tokio::time; let duration : time::Duration = time::Duration::from_millis(1000); |
■ join! 매크로를 사용해 비동기 병렬 처리 완료시까지 대기하는 방법을 보여준다. ▶ Cargo.toml
|
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } |
▶ src/main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
use tokio::time; async fn print_message_async(second_count : u64, message : &str) { time::sleep(time::Duration::from_secs(second_count)).await; println!("{} : {}", second_count, message); } #[tokio::main] async fn main() { tokio::spawn(print_message_async(3, "메시지 #1")); tokio::spawn(print_message_async(2, "메시지 #2")); tokio::spawn(print_message_async(1, "메시지 #3")); time::sleep(time::Duration::from_secs(4)).await; println!("------------"); tokio::join! ( print_message_async(2, "메시지 #4"), print_message_async(3, "메시지 #5"), print_message_async(1, "메시지 #6") ); } |
test_project.zip
■ spawn 함수를 사용해 비동기 병렬 처리하는 방법을 보여준다. ▶ Cargo.toml
|
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } |
▶ src/main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
use tokio::time; async fn print_message_async(second_count : u64, message : &str) { time::sleep(time::Duration::from_secs(second_count)).await; println!("{} : {}", second_count, message); } #[tokio::main] async fn main() { tokio::spawn(print_message_async(3, "메시지 #1")); tokio::spawn(print_message_async(2, "메시지 #2")); tokio::spawn(print_message_async(1, "메시지 #3")); time::sleep(time::Duration::from_secs(4)).await; } |
test_project.zip
■ 블럭문을 사용해 비동기 처리를 하는 방법을 보여준다. ▶ Cargo.toml
|
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } |
▶ src/main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
use tokio::time; #[tokio::main] async fn main() { for i in 1..=3 { println!("#{} START TO GET MESSAGE", i); let message = async { time::sleep(time::Duration::from_secs(1)).await; return String::from(format!("MESSAGE {}", i)); }.await; println!("{}", message); } } |
test_project.zip
■ tokio 크레이트를 사용해 반환값을 갖는 비동기 처리를 하는 방법을 보여준다. ▶ Cargo.toml
|
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } |
▶ src/main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
use tokio::time; #[tokio::main] async fn main() { for i in 1..=3 { println!("#{} START TO GET MESSAGE", i); let message : String = get_message_async(i).await; println!("{}", message); } } async fn get_message_async(value : i32) -> String { time::sleep(time::Duration::from_secs(1)).await; return String::from(format!("MESSAGE {}", value)); } |
test_project.zip
■ sleep 함수를 사용해 실행을 일정 시간 동안 대기하는 방법을 보여준다. ▶ 예제 코드 (RS)
|
use tokio::time; let duration : time::Duration = time::Duration::from_secs(1); time::sleep(duration).await; |
■ Duration 구조체의 from_secs 연관 함수를 사용해 초단위 Duration 객체를 구하는 방법을 보여준다. ▶ 예제 코드 (RS)
|
use tokio::time; let duration : time::Duration = time::Duration::from_secs(1); |
■ tokio 크레이트를 사용해 비동기 처리를 하는 방법을 보여준다. ▶ Cargo.toml
|
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } |
▶ src/main.rs
|
#[tokio::main] async fn main() { let future = print_message_async("메시지 #1"); println!("메시지 #2"); future.await; } async fn print_message_async(message : &'static str) { println!("{}", message); } |
test_project.zip
■ tokio 크레이트를 설치하는 방법을 보여준다. ▶ Cargo.toml
|
... [dependencies] tokio = { version = "1", features = ["full"] } |
■ spawn 함수를 사용해 쓰레드로 병렬 계산을 처리하는 방법을 보여준다. ▶ 예제 코드 (RS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
use std::sync::mpsc; use std::thread; use std::time; fn main() { let index_array : [i64; 7] = [43, 42, 20, 39, 37, 35, 30]; let instant : time::Instant = time::Instant::now(); let (tx, rx) = mpsc::channel::<(i64, i64)>(); for index in index_array { let sender : mpsc::Sender<(i64, i64)> = tx.clone(); thread::spawn ( move || { let fibonacci_sequence : i64 = calculate_fibonacci_sequence(index); sender.send((index, fibonacci_sequence)).unwrap(); } ); } let mut job_count : usize = index_array.len(); loop { if let Ok((index, fibonacci_sequence)) = rx.recv() { job_count -= 1; println!("[결과] (남은 계산 : {}) {} 번째 피보나치 수 : {} ", job_count, index, fibonacci_sequence); if job_count <= 0 { let duration : time::Duration = instant.elapsed(); println!("실행 시간 : {:?}", duration); break; } } thread::sleep(time::Duration::from_millis(300)); } } fn calculate_fibonacci_sequence(index : i64) -> i64 { if index == 1 { return 0; } if index == 2 { return 1; } return calculate_fibonacci_sequence(index - 2) + calculate_fibonacci_sequence(index - 1); } |
■ MPSC 채널 메커니즘을 사용해 스레드 간 데이터 공유하기 ▶ 예제 코드 (RS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
use std::sync::mpsc; use std::thread; use std::time; fn send_message(source_message : &str, sender : mpsc::Sender<String>) { let message_array : [&str; 5] = ["메시지 1", "메시지 2", "메시지 3", "메시지 4", "메시지 5"]; for message in message_array { let target_message : String = format!("{} : {}", source_message, message); sender.send(target_message).unwrap(); thread::sleep(time::Duration::from_millis(1000)); } sender.send("중단".to_string()).unwrap(); } fn main() { let (tx, rx) = mpsc::channel::<String>(); let sender1 : mpsc::Sender<String> = tx.clone(); thread::spawn(|| { send_message("스레드 1", sender1); }); let sender2 : mpsc::Sender<String> = tx.clone(); thread::spawn(|| { send_message("스레드 2", sender2) }); loop { let message : String = rx.recv().unwrap(); println!("[수신] {}", message); if message == "중단" { break; } } } /* [수신] 스레드 1 : 메시지 1 [수신] 스레드 2 : 메시지 1 [수신] 스레드 2 : 메시지 2 [수신] 스레드 1 : 메시지 2 [수신] 스레드 1 : 메시지 3 [수신] 스레드 2 : 메시지 3 [수신] 스레드 1 : 메시지 4 [수신] 스레드 2 : 메시지 4 [수신] 스레드 1 : 메시지 5 [수신] 스레드 2 : 메시지 5 [수신] 중단 */ |
■ channel 함수를 사용해 MPSC 채널 객체를 생성하는 방법을 보여준다. ▶ 예제 코드 (RS)
|
use std::sync::mpsc; let (tx, rx) = mpsc::channel::<String>(); |
■ spawn 함수를 사용해 쓰레드를 생성하는 방법을 보여준다. ▶ 예제 코드 (RS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
use std::thread; use std::time; fn print_message(message : &str) { for i in 1..=3 { println!("{} : i = {}", message, i); thread::sleep(time::Duration::from_millis(1000)); } } fn main() { print_message("메인 스레드 1"); thread::spawn(|| { print_message("스레드 1"); }); thread::spawn(|| { print_message("스레드 2"); }); thread::spawn(|| { print_message("스레드 3"); }); print_message("메인 스레드 2"); } /* 메인 스레드 1 : i = 1 메인 스레드 1 : i = 2 메인 스레드 1 : i = 3 메인 스레드 2 : i = 1 스레드 1 : i = 1 스레드 2 : i = 1 스레드 3 : i = 1 스레드 1 : i = 2 메인 스레드 2 : i = 2 스레드 3 : i = 2 스레드 2 : i = 2 메인 스레드 2 : i = 3 스레드 3 : i = 3 스레드 1 : i = 3 스레드 2 : i = 3 */ |