3

비동기, 작업, 람다 등을 이해하기 시작했으며 원하는대로 작동시키지 못했습니다. 아래 코드를 사용하여 btnDoWebRequest를 잠 그려면 Tasks로 WebRequests의 번호를 알 수 있습니다. 그리고 일단 모든 작업이 완료되면 btnDoWebRequest를 잠금 해제합니다. 그러나 나는 단지 최대 3 개 또는 어떤 숫자의 작업을 한 번에 실행하고 싶습니다. 이것은 부분적으로는 Have a set of Tasks with only X running at a time입니다.한 번에 최대 실행 작업을 설정하는 동안 여러 비동기 작업을 기다리는 중

하지만 코드를 여러 가지 방법으로 시도하고 수정 한 후에는 즉시 즉시 건너 뛰고 btnDoWebRequest를 다시 사용하게됩니다. 물론 VS는 ".ContinueWith ((task)"와 "await Task.WhenAll (requestInfoList.Select (async i =>)"에서 비동기에 대한 대기 필요성에 대해 경고합니다. 필요한 것을 넣는 방법. 물론 내가 아직도 배우고있는 한,이 모든 일을 잘못하고 모든 일을 다시 할 필요가있는 좋은 기회입니다. 그래서 어떤 도움이라도 크게 감사 할 것입니다.

private SemaphoreSlim maxThread = new SemaphoreSlim(3); 
    private void btnDoWebRequest_Click(object sender, EventArgs e) 
    { 
     btnDoWebRequest.Enabled = false; 
     Task.Factory.StartNew(async() => await DoWebRequest()).Wait(); 
     btnDoWebRequest.Enabled = true; 
    } 

    private async Task DoWebRequest() 
    { 
     List<requestInfo> requestInfoList = new List<requestInfo>(); 
     for (int i = 0; dataRequestInfo.RowCount - 1 > i; i++) 
     { 
      requestInfoList.Add((requestInfo)dataRequestInfo.Rows[i].Tag); 
     } 
     await Task.WhenAll(requestInfoList .Select(async i => 
     { 
      maxThread.Wait(); 
      Task.Factory.StartNew(() => 
      { 
       var task = Global.webRequestWork(i); 
      }, TaskCreationOptions.LongRunning).ContinueWith((task) => maxThread.Release()); 
     })); 
    } 
+0

이벤트 핸들러는 아마도, 비동기 키워드를 가지고 있지 않기 때문에 컴파일러 오류가 있습니까? 이 없이는 처리기의 await 키워드가 장기간 작동이 완료 될 때까지 기다리는 것이 좋습니다. – brumScouse

+0

그리고 모든 것이 완료 될 때까지 이벤트 핸들러가 차단되므로 서버의 활성화 된 상태를 토글하고 코드를 디버깅하는 경우에만이를 증명할 수 있습니다. – brumScouse

답변

9

첫째, 기본적으로 Task.Factory.StartNew을 사용하지 않습니다. 사실,이 async 코드를 피해야한다. 당신이 Task.Run를 사용 후, 백그라운드 스레드에서 코드를 실행해야하는 경우.

귀하의 경우 Task.Run (또는 Task.Factory.StartNew)을 사용할 필요가 없습니다.

최저 수준에서 시작하여 진행하십시오. 이미 비동기 웹 요청 메소드가 있습니다. 이름을 WebRequestAsync으로 바꿔 Task-based Asynchronous Programming 명명 규칙을 따르십시오.

다음, SemaphoreSlim비동기 API를 사용하여 스로틀 :

await maxThread.WaitAsync(); 
try 
{ 
    await Global.WebRequestWorkAsync(i); 
} 
finally 
{ 
    maxThread.Release(); 
} 

각 요청 정보를 원하시면 그렇게 (더 백그라운드 스레드가 필요하지 않음을 유의) :

마지막으로
private async Task DoWebRequestsAsync() 
{ 
    List<requestInfo> requestInfoList = new List<requestInfo>(); 
    for (int i = 0; dataRequestInfo.RowCount - 1 > i; i++) 
    { 
    requestInfoList.Add((requestInfo)dataRequestInfo.Rows[i].Tag); 
    } 
    await Task.WhenAll(requestInfoList.Select(async i => 
    { 
    await maxThread.WaitAsync(); 
    try 
    { 
     await Global.WebRequestWorkAsync(i); 
    } 
    finally 
    { 
     maxThread.Release(); 
    } 
    })); 
} 

, UI에서 호출하십시오 (배경 스레드가 필요 없음).

private async void btnDoWebRequest_Click(object sender, EventArgs e) 
{ 
    btnDoWebRequest.Enabled = false; 
    await DoWebRequestsAsync(); 
    btnDoWebRequest.Enabled = true; 
} 

요약하면 필요하면 Task.Run 만 사용하십시오. Task.Factory.StartNew을 사용하지 말고 Wait (대신 await을 사용하십시오)을 사용하지 마십시오. 나는 내 블로그에 더 많은 정보를 가지고 async intro을 가지고있다.

1

이 코드 문제 몇 가지가 있습니다 태스크에

  1. 사용하여 대기하는 것은() 따라서 만 모든 것이 완료하고 버튼이 다시 활성화 될 때 UI가 반응 통지, 동 기적으로 일을 실행 같다 . 진정으로 비동기로 실행하려면 비동기 메소드를 기다려야합니다. 따라서 메소드가 웹 요청과 같은 IO 바운드 작업을 수행하는 경우 Task.Factory.StartNew를 사용하여 새 스레드 풀 스레드를 돌리는 것이 중복되어 자원 낭비입니다.

  2. 단추 클릭 이벤트 처리기를 비동기로 표시해야 메서드 내부에서 기다릴 수 있습니다.

  3. 새로운 SemaphoreSlim WaitAsync를 사용하여 코드를 명확하게 정리하고 LINQ 쿼리로 대체했습니다.처음 두 점만 취하여 코드에 적용하면됩니다.

    private SemaphoreSlim maxThread = new SemaphoreSlim(3); 
    
    private async void btnDoWebRequest_Click(object sender, EventArgs e) 
    { 
        btnDoWebRequest.Enabled = false; 
        await DoWebRequest(); 
        btnDoWebRequest.Enabled = true; 
    } 
    
    private async Task DoWebRequest() 
    { 
        List<requestInfo> requestInfoList = new List<requestInfo>(); 
    
        var requestInfoList = dataRequestInfo.Rows.Select(x => x.Tag).Cast<requestInfo>(); 
    
        var tasks = requestInfoList.Select(async I => 
        { 
         await maxThread.WaitAsync(); 
         try 
         { 
          await Global.webRequestWork(i); 
         } 
         finally 
         { 
          maxThread.Release(); 
         } 
        }); 
    
        await Task.WhenAll(tasks); 
    
0

나는이에 대한 확장 방법을 만들었습니다.

그것은 다음과 같이 사용할 수 있습니다

var tt = new List<Func<Task>>() 
{ 
    () => Thread.Sleep(300), //Thread.Sleep can be replaced by your own functionality, like calling the website 
    () => Thread.Sleep(800), 
    () => Thread.Sleep(250), 
    () => Thread.Sleep(1000), 
    () => Thread.Sleep(100), 
    () => Thread.Sleep(200), 
}; 
await tt.WhenAll(3); //this will let 3 threads run, if one ends, the next will start, untill all are finished. 

확장자 방법 :

public static class TaskExtension 
{ 
    public static async Task WhenAll(this List<Func<Task>> actions, int threadCount) 
    { 
     var _countdownEvent = new CountdownEvent(actions.Count); 
     var _throttler = new SemaphoreSlim(threadCount); 

     foreach (Func<Task> action in actions) 
     { 
      await _throttler.WaitAsync(); 

      Task.Run(async() => 
      { 
       try 
       { 
        await action(); 
       } 
       finally 
       { 
        _throttler.Release(); 
        _countdownEvent.Signal(); 
       } 
      }); 
     } 

     _countdownEvent.Wait(); 
    } 
} 
관련 문제