문제

문제

내가 여기서 이야기 할 코드는 F#에 썼지 만, f#의 특정성에 따라 .NET 4 프레임 워크를 기반으로합니다 (적어도 그렇게 보입니다!).

디스크에 네트워크에서 업데이트 해야하는 데이터 조각이 있습니다. 최신 버전을 디스크에 저장합니다.

type MyData =
    { field1 : int;
      field2 : float }

type MyDataGroup =
    { Data : MyData[];
      Id : int }

// load : int -> MyDataGroup
let load dataId =
    let data = ... // reads from disk
    { Data = data;
      Id = dataId }

// update : MyDataGroup -> MyDataGroup
let update dg =
    let newData = ... // reads from the network and process
                      // newData : MyData[]

    { dg with Data = dg.Data
                     |> Seq.ofArray
                     |> Seq.append newData
                     |> processDataSomehow
                     |> Seq.toArray }

// save : MyDataGroup -> unit
let save dg = ... // writes to the disk

let loadAndSaveAndUpdate = load >> update >> save

문제는 loadAndSaveAndUpdate 내 모든 데이터, 나는 함수를 실행해야 할 것이다 많은 타임스:

{1 .. 5000} |> loadAndSaveAndUpdate

각 단계는 할 것입니다

  • 일부 디스크 IO,
  • 일부 데이터 크런치,
  • 일부 네트워크 IO (많은 대기 시간 가능성),
  • 더 많은 데이터 크런치,
  • 그리고 일부 디스크 io.

이 작업을 어느 정도 동시에 수행하는 것이 좋지 않습니까? 불행히도, 내 독서 및 구문 분석 기능 중 어느 것도 "비동기식 플로우 준비"가 아닙니다.

내가 생각한 첫 번째 (아주 좋지 않은) 솔루션

작업

내가 한 첫 번째 일은 Task[] 그리고 그들 모두를 시작하십시오 :

let createTask id = new Task(fun _ -> loadAndUpdateAndSave id)
let tasks = {1 .. 5000}
            |> Seq.map createTask
            |> Seq.toArray

tasks |> Array.iter (fun x -> x.Start())
Task.WaitAll(tasks)

그런 다음 CTRL+ESC를 눌러 사용중인 스레드 수를 확인했습니다. 15, 17, ..., 35, ..., 170, ... 신청서를 죽일 때까지! 뭔가 잘못되고있었습니다.

평행한

나는 거의 똑같은 일을했지만 사용했습니다 Parallel.ForEach(...) 그리고 결과는 동일했다 : 많은 실, 많은 실.

작동하는 솔루션 ... 일종의

그런 다음 시작하기로 결정했습니다 n 스레드, Task.WaitAll(of them), 그럼 다른 n, 더 이상 사용 가능한 작업이 없을 때까지.

이것은 작동하지만 문제는 처리가 완료되었을 때 n-1 작업, 기다릴 것입니다. 기다릴 것입니다. 많은 네트워크 대기 시간으로 인해 차단을 주장하는 마지막 작업이 기다릴 것입니다. 이것은 좋지 않습니다!

그래서, 이 문제를 어떻게 공격하겠습니까?? 비동기 워크 플로 (및이 경우 비 동기 기능을 조정하는 방법), 병렬 확장, 이상한 병렬 패턴 등과 관련된 다른 솔루션을 보는 것이 좋습니다.

감사.

도움이 되었습니까?

해결책

개별 작업이 적시에 완료되고 있다고 확신합니까? 나는 둘 다 믿습니다 Parallel.ForEach 그리고 Task 클래스는 이미 .NET ThreadPool을 사용합니다. 작업은 일반적으로 수명이 짧은 작업 항목이어야하며,이 경우 ThreadPool은 소수의 실제 스레드 만 스폰되지만 작업이 진행되지 않고 다른 작업이 대기하는 경우 사용 된 스레드의 수는 꾸준히 증가합니다. 최대 값 (기본적으로 250/프로세서 .NET 2.0 SP1에서는 프레임 워크의 다른 버전에서 다릅니다). 또한 (적어도 .NET 2.0 SP1에서) 새 스레드 생성이 초당 2 개의 새로운 스레드로 조절되므로보고있는 스레드 수에 올라가는 것은 작업이 짧은 양으로 완료되지 않았 음을 나타냅니다. 시간 (그래서 비난을 고정하는 것이 완전히 정확하지 않을 수 있습니다. Parallel.ForEach).

나는 브라이언의 사용 제안이라고 생각합니다 async 워크 플로우는 특히 장기적인 작업의 출처가 IO 인 경우 좋은 것입니다. async IO가 완료 될 때까지 스레드를 스레드 풀로 되돌립니다. 또 다른 옵션은 작업이 빠르게 완료되지 않고 많은 스레드의 산란을 허용하는 것입니다 (사용하여 어느 정도 제어 할 수 있습니다. System.Threading.ThreadPool.SetMaxThreads) - 상황에 따라 많은 스레드를 사용하는 것은 큰 문제가 아닐 수도 있습니다.

다른 팁

병렬 탑 병렬 메소드 호출에 의해 실행되는 동시 작업 수를 제한합니다.

'Async'를 사용하면 다양한 I/O 통화가 'Sea'인 동안 실을 태우지 않고 I/O 바운드 작업을 수행 할 수 있으므로 첫 번째 제안이 될 것입니다. 코드를 일반적으로 라인을 따라 비동기로 변환하는 것이 간단해야합니다.

  • 각 기능 본문을 감습니다 async{...}, 추가하다 return 필요한 곳에
  • 라이브러리를 통해 아직 라이브러리에 있지 않은 모든 I/O 프리미티브의 비동기 버전 생성 Async.FromBeginEnd
  • 양식의 호출을 전환합니다 let r = Foo() 에게 let! r = AsyncFoo()
  • 사용 Async.Parallel 5000 개의 비동기 물체를 병렬로 실행되는 단일 비동기로 변환하려면

이를위한 다양한 튜토리얼이 있습니다. 그러한 웹 캐스트 중 하나입니다 여기.

항상 사용할 수 있습니다 ThreadPool.

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

원래:

  1. 스레드 풀을 만듭니다
  2. 최대 스레드 수를 설정하십시오
  3. 사용하는 모든 작업을 대기하십시오 QueueUserWorkItem(WaitCallback)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top