2013-04-29 2 views
0

Parallel.ForEach 루프에서 공유 리소스에 대한 액세스를 어떻게 제어합니까? 여러 파일을 동시에 다운로드하려고하는데 실패한 다운로드에 대한 정보를 캡처하여 나중에 사용자가 다운로드를 다시 시도 할 수있게하려고합니다. 그러나 하나 이상의 스레드가 동시에 다른 스레드에 의해 쓰여지는 동안 파일에 액세스하려고 시도하기 때문에 둘 이상의 다운로드가 동시에 실패하는 경우 응용 프로그램에서 예외가 발생합니다.Parallel.ForEach의 공유 리소스

아래 코드에서 RepeateRequestPath에서 파일에 대한 액세스를 제어하는 ​​방법을 알고 싶습니다. RequestSet은 다운로드하려고하는 리소스의 ID를 나타내는 문자열 목록입니다.

Dim DownloadCnt As Integer = 0 
Dim ParallelOpts As New ParallelOptions() 
ParallelOpts.MaxDegreeOfParallelism = 4 
Parallel.ForEach(RequestSets, ParallelOpts, Sub(RequestSet) 
     Try 
      DownloadCnt += 1 
      Dim XmlUrl As String = String.Format("{0}{1}{2}", "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=", String.Join(",", RequestSet), "&retmode=xml&rettype=abstract") 
      DownloadFile(XmlUrl, String.Format("{0}\TempXML{1}.xml", XMLCacheDir, DownloadCnt)) 
     Catch ex As WebException 
      Using Response As WebResponse = ex.Response 
       Dim statCode As Integer = CInt(DirectCast(Response, HttpWebResponse).StatusCode) 
       MessageBox.Show(String.Format("Failed to retrieve XML due to HTTP error {0}. Please hit the 'Retrieve XML' button to re-run retrieval after the current set is complete.", statCode)) 
       If Not File.Exists(RepeatRequestPath) Then 
        File.WriteAllLines(RepeatRequestPath, RequestSet) 
       Else 
        File.AppendAllLines(RepeatRequestPath, RequestSet) 
       End If 
      End Using 
     End Try 
    End Sub) 

답변

2

VB.NET에서 공유 리소스를 보호하는 일반적인 방법은 SyncLock입니다.

Dim lock = New Object 

를 다음 루프 내부의 것을 사용합니다 :

그래서, 당신은 Parallel.ForEach() 루프 전에 잠금 객체를 생성 할

SyncLock lock 
    File.AppendAllLines(RepeatRequestPath, RequestSet) 
End SyncLock 

또한주의 당신이 경우에도 AppendAllLines()을 사용할 수 있습니다 파일이 아직 존재하지 않으므로 그 파일을 검사 할 필요가 없습니다.

+0

완벽! 그래도 빠른 질문. Try-Catch 블록의 Try 섹션에서 다른 파일에 대해 동일한 잠금을 사용할 수 있습니까? 아니면 별도의 객체를 인스턴스화해야합니까? 나는 DownloadFile을 호출 한 후에 다른 파일로 똑같은 작업을하고 싶다. – user667118

+0

@ user667118 가능합니다. 또 다른 질문은 하나의 스레드가 하나의 파일에 쓰기를 원할 경우 다른 파일에 쓰는 다른 스레드를 기다려야한다는 것을 의미하기 때문에 그렇게해야하는지 여부입니다. – svick

+0

파일로 쓰는 데 그리 오래 걸리지 않기 때문에별로 문제가되지 않을 것이라고 생각합니다. 전반적으로이 코드는 이전에 동기식으로 실행되었으므로 많은 시간을 절약 할 수 있습니다. – user667118

1

공유 리소스에 대한 액세스를 제어하려면 세마포를 사용해야합니다. 한 번에 하나의 스레드 만 오류 파일에 액세스하기를 원하므로 1 스레드 만 허용하도록 세마포를 초기화합니다. _pool.WaitOne 호출은 세마포를 점유하고 파일 작성/쓰기를 완료 한 후에 해제해야합니다.

Private Shared _pool As Semaphore 
_pool = = New Semaphore(0, 1) 

Dim DownloadCnt As Integer = 0 
Dim ParallelOpts As New ParallelOptions() 
ParallelOpts.MaxDegreeOfParallelism = 4 
Parallel.ForEach(RequestSets, ParallelOpts, Sub(RequestSet) 
     Try 
      DownloadCnt += 1 
      Dim XmlUrl As String = String.Format("{0}{1}{2}", "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=", String.Join(",", RequestSet), "&retmode=xml&rettype=abstract") 
      DownloadFile(XmlUrl, String.Format("{0}\TempXML{1}.xml", XMLCacheDir, DownloadCnt)) 
     Catch ex As WebException 
      Using Response As WebResponse = ex.Response 
       Dim statCode As Integer = CInt(DirectCast(Response, HttpWebResponse).StatusCode) 
       MessageBox.Show(String.Format("Failed to retrieve XML due to HTTP error {0}. Please hit the 'Retrieve XML' button to re-run retrieval after the current set is complete.", statCode)) 
       _pool.WaitOne() 
       Try 
        If Not File.Exists(RepeatRequestPath) Then 
         File.WriteAllLines(RepeatRequestPath, RequestSet) 
        Else 
         File.AppendAllLines(RepeatRequestPath, RequestSet) 
        End If 
       Catch ex as Exception 
        'Do some error handling here. 
       Finally 
        _pool.Release() 
       End Try 
      End Using 
     End Try 
    End Sub) 
+0

좋은 지적 - 출시가 요청되지 않았습니다. try catch 블록이 catch 블록에 얼마나 수용 가능한지 확실하지 않습니다. –

+0

짧기 때문에 svick의 해결책을 선택했지만 도움을 주셔서 감사합니다! – user667118

0

svick의 솔루션이 거의 옳습니다. 그러나 공유 변수에 대한 액세스를 보호해야하는 경우 클래스 레벨에서 공유 객체로 잠금 객체를 선언해야합니다.

이 제대로 작동 :

Friend Class SomeClass 
    Private Shared _lock As New Object 

    Private Shared sharedInt As Integer = 0 

    Sub Main() 
     SyncLock _lock 
      sharedInt += 1 
     End SyncLock 
    End Sub 
End Class 

가 아닌 공유 잠금 개체를 사용하는 경우의 SyncLock은 동일한 인스턴스 내에서가 아닌 인스턴스에서 여러 액세스하는 스레드에서 변수를 보호 할 것입니다.