2011-01-17 9 views
1

경량 스레드 (파이버) 용 라운드 로빈 스케줄러를 작성하려고합니다. 가능한 한 많은 수의 동시에 계획된 광섬유를 처리 할 수 ​​있도록 확장해야합니다. 나는 또한 실행 루프가 아닌 스레드에서 파이버를 스케줄링 할 수 있어야하며, 임의의 스레드에서도 스케줄링을 취소해야합니다 (실행 루프에서 스케줄을 취소 할 수는 있지만).가벼운 스레드 안전 스케줄러에 대한 제안

나의 현재 아이디어는 각 파이버가 노드이고 스케줄러가 현재 노드에 대한 참조를 보유하는 원형 이중 연결 목록을 갖는 것입니다. 당신이 볼 수 있듯이

using Interlocked = System.Threading.Interlocked; 

public class Thread { 
    internal Future current_fiber; 

    public void RunLoop() { 
     while (true) { 
      var fiber = current_fiber; 
      if (fiber == null) { 
       // block the thread until a fiber is scheduled 
       continue; 
      } 
      if (fiber.Fulfilled) 
       fiber.Unschedule(); 
      else 
       fiber.Resume(); 

      //if (current_fiber == fiber) current_fiber = fiber.next; 
      Interlocked.CompareExchange<Future> (ref current_fiber, fiber.next, fiber);  
     } 
    } 
} 

public abstract class Future { 
    public bool Fulfilled { get; protected set; } 
    internal Future previous, next; 

    // this must be thread-safe 
    // it inserts this node before thread.current_fiber 
    // (getting the exact position doesn't matter, as long as the 
    // chosen nodes haven't been unscheduled) 
    public void Schedule (Thread thread) { 
     next = this; // maintain circularity, even if this is the only node 
     previous = this; 
    try_again: 
     var current = Interlocked.CompareExchange<Future> (ref thread.current_fiber, this, null); 
     if (current == null) 
      return; 

     var target = current.previous; 
     while (target == null) { 
      // current was unscheduled; negotiate for new current_fiber 
      var potential = current.next; 
      var actual = Interlocked.CompareExchange<Future> (ref thread.current_fiber, potential, current); 
      current = (actual == current? potential : actual); 
      if (current == null) 
       goto try_again; 
      target = current.previous; 
     } 
     // I would lock "current" and "target" at this point. 
     // How can I do this w/o risk of deadlock? 
     next = current; 
     previous = target; 
     target.next = this; 
     current.previous = this; 
    } 

    // this would ideally be thread-safe 
    public void Unschedule() { 
     var prev = previous; 
     if (prev == null) { 
      // already unscheduled 
      return; 
     } 
     previous = null; 
     if (next == this) { 
      next = null; 
      return; 
     } 
     // Again, I would lock "prev" and "next" here 
     // How can I do this w/o risk of deadlock?    
     prev.next = next; 
     next.previous = prev; 
    } 

    public abstract void Resume(); 
} 

것은, 내 난제 내가 잠금의 순서를 보장 할 수없는, 그래서 교착 상태를 위험없이 하나 개 이상의 노드를 잠글 수 있다는 것입니다 : 이것은 내가 지금까지있는 것입니다. 아니면 내가 할 수 있니? 잠금 경합의 양이 극단적이기 때문에 Thread 객체에 대한 전역 잠금을 원하지 않습니다. 게다가 삽입 위치에 대해서는 특별히 신경 쓰지 않습니다. 각 노드를 개별적으로 잠그면 Schedule()은 Monitor.TryEnter와 같은 것을 사용할 수 있으며 잠금 해제 된 노드를 찾을 때까지 계속 목록을 계속 걷습니다.

전반적으로 언급 한 요구 사항을 충족하는 한 특정 구현에 투자하지 않습니다. 어떤 아이디어라도 대단히 감사하겠습니다. 감사!

EDIT - 의견에 의하면 사람들이 내가 winapi 섬유 (나는 그렇지 않다)에 대해 말하고 있다고 생각합니다. 간단히 말해서, 스레드에서 스레드를 차례로 실행하도록 코드 비트를 예약하는 것입니다. TPL/Async CTP와 비슷하지만 AFIK는 UI 스레드가 아닌 한 같은 스레드에서 연속성을 보장하지 않습니다. 위의 방법을 구현하는 방법에 대한 제안을 대체 할 수 있지만 "섬유를 사용하지 마십시오"라고 말하지 마십시오.

+0

스레드 풀 또는 Backgroundworker에 어떤 문제가 있습니까? –

+0

OS 레벨 스레드보다 저렴한 가격으로 촬영하고 있습니다. 나는 수만 (또는 그 이상)의 섬유를 가지고 일정을 세우고 스케쥴을 잡을 생각을한다. – chkn

+0

불쾌감은 없지만, 미래를 위해 나는 성가심, 불안정, 그리고 실패를 예견한다. 필자는 winapi에 섬유를 쓰려고 시도한 적이 없었습니다. 필자는 의심의 여지없이 적은 양의 프로그래머 및/또는 프로그램이 있다고 의심합니다. 그 이유는 유스 케이스에 맞는 프로그램의 양이 거의 0이되기 때문입니다.모든 프로그램의 000001 %. (기술적 인 지식이 부족한 것 같습니다). –

답변

0

.NET에서 Fibers를 사용하지 말고 Joe Duffy가이 주제에 관해 읽으십시오. 대신 사용자 모드 스케줄러의 올바른 구현을 위해 TPL을 사용하십시오.

+0

나는 winapi 섬유를 사용하지 않습니다. 나는 약간 더 명확하게하기 위해 나의 질문을 편집했다. – chkn

2

작업 병렬 라이브러리를 사용하십시오.

MSDN에 표시된대로 TaskScheduler 사용자 지정을 만듭니다. 사용자 정의 작업 스케줄러에서 하나의 스레드 만 원한다면 하나의 스레드 만 가질 수 있습니다.

작업을 인라인으로 예약하지 않으려면 protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)을 무시하고 false을 반환하십시오.

0

단일 스레드에서 실행하는 것이 필요하지 않으며 한 번에 하나의 작업 만 완료까지 실행됩니다. 이 경우 TPL samples을 다운로드하십시오. MaximumConcurrency가 1 인 LimitedConcurrencyLevelTaskScheduler를 사용하십시오.

0

lock-free message queue을 사용하여 Futures를 대기열에 추가 할 수 있습니다.

순환 데이터 구조를 유지하는 대신 Resume()을 호출하기 전에 대기열에서 Future를 가져 와서 작업이 완료되면 추가해야합니다.