2017-02-14 4 views
0

現在、WGETを使用して複数のcsvファイルを異なるサーバーからダウンロードするプログラムをコーディングしています。 WaitForExit(10000)メソッドを使用するプロセスとしてWGETを呼び出すため、すべてのダウンロードは新しいスレッドです.WGETはダウンロードする時間が10秒です。その時間内にダウンロードが完了しなかった場合、サーバーは時間内に応答しなかったため、スレッドは強制終了されます。GUI上のアクションのマルチスレッドの順序

また、私のプログラムが現在行っていることと、どのスレッドがどのステータスで終了したのかを記録するリストビューがあります。

だから(lvw_logが私のリストビューで)ログに記録する私の方法です:

Public Delegate Sub LogDelegate(ByVal Text As String) 

Public Sub Log(Text As String) 
    If lvw_Log.InvokeRequired Then 
     lvw_Log.BeginInvoke(New LogDelegate(AddressOf Log), New Object() {Text}) 
    Else 
     lvw_Log.Items.Add(DateTime.Now + ": " + Text) 
     lvw_Log.TopIndex = lvw_Log.Items.Count - 1 
     lvw_Log.Refresh() 
    End If 

End Sub 

テキストがWGET-スレッドの1から私のListViewに追加する必要がある場合、デリゲートが呼び出されます。 'p'は、スレッドにアクセス可能な変数のセットを渡すための、自分のクラスのオブジェクトです。

私はWGETThreadArrayと呼ばれるのArrayList内のすべてのスレッドを格納します。

Dim WGETThreadArray As New ArrayList 

For i = 0 to NumberOfFilesToDownload - 1 
    Dim WGETThread As New System.Threading.Thread(AddressOf StartWGET) 

    WGETThreadArray.Add(WGETThread) 
    Log("Starting thread " + i.ToString) 
    WGETThreadArray(i).Start(p) 
Next 

今、私はすべてのスレッドが終了するか、中止されるのを待つしたい:

Log("Waiting for threads to finish") 
For i = 0 To WGETThreadArray.Count - 1 

    WGETThreadArray(i).Join() 

Next 
    Log("All threads closed") 
    Log("Downloaded all DB-Info-Files") 

スレッド(メソッドStartWGET)されますこれは:

Public Sub StartWGET(p As Object) 
    'this method is called for each thread to parallely download the necessary files 

    Dim procInfo As New ProcessStartInfo(p.PathToWgetExe, p.ArgumentString) 
    procInfo.CreateNoWindow = False 
    procInfo.UseShellExecute = True 

    Dim WGETProcessHandler As System.Diagnostics.Process = System.Diagnostics.Process.Start(procInfo) 

    If Not WGETProcessHandler.WaitForExit(10000) Then   'if WGET doesn't finish within '10000' milliseconds, the thread gets killed 

     WGETProcessHandler.Kill() 
     Log("DB " + p.DBName + " was not loaded. Thread " + p.ThreadIndex.ToString + " killed ") 
     DatabaseArray(p.ThreadIndex).isLoaded = False 
     WGETThreadArray(p.ThreadIndex).Abort() 

    Else 
     DatabaseArray(p.ThreadIndex).isLoaded = True 
     Log(p.URL + " downloaded. Thread " + p.ThreadIndex.ToString + " ended successfully.") 

    End If 
End Function 

ご覧のとおり、「ログ」メソッドがスレッド内で呼び出されます。問題は、メインスレッドは常に他のスレッドの前にListViewに書き込むことです。だから私は、 "スレッドが正常に終了しました"のようなメッセージの前に "すべてのスレッドが閉じられました"という行を見る。私はforループで.Join()メソッドを使っていましたが。すべてのスレッドが終了するまで待ちます。

他のすべてのWGETスレッドが完了するまでメインスレッドを一時停止させるにはどうすればよいですか。また、メインスレッドが引き継ぎ、すべてのスレッドが終了したことを知らせる前に、どうすればそれらをログに記録できますか?

私は説明するのが難しいことを知っており、私は自分自身を明確にしたいと思っています。そうでない場合は、私にもう一度聞いてください。

+1

スレッドの代わりにスレッドを使用する特別な理由はありますか?処理が簡単なタスク? –

+0

私はスレッドを使用しました。なぜなら私は正直であると分かっていなかったからです。しかし、なぜこれはスレッドではうまくいかないでしょうか? – StewPit

+0

'Wait Handles'を使用してください – Xaqron

答えて

0

メインスレッドはJoin()呼び出しによってブロックされるため、メインスレッドに切り替えるために、LogのBeginInvokeはメインスレッドが使用可能になるのを待たなければなりません(すべての結合後)。メインスレッドが利用可能になった後、Logへの実際の呼び出しが実行されます。スレッドを待って「すべてのスレッドを閉じました」というメッセージが表示されたら UIレンダリングを実行するメインスレッドを解放するために、新しいスレッドのワーカースレッドを待つように呼び出します。これは重要な原則です:応答性の高いUIを持つためには、メインスレッドは非常にわずかな作業しか行わず、は決してをブロックしないでください。

0
Log("Waiting for threads to finish") 
Dim SomeAlive as boolean 
do 
    Threading.Thread.Sleep(100) 
    SomeAlive = False 
    For i as integer = 0 To WGETThreadArray.Count - 1 
     if WGETThreadArray(i).IsALive then 
      SomeALive = True 
      Exit For 
     end if 
    Next 
Loop While SomeAlive 

スレッドはとにかくルーチンの最後に中断されますので、あなたの文

WGETThreadArray(p.ThreadIndex).Abort() 

は無意味です。

また、複数のファイルをダウンロードするための複数のプロセスを起動すると、一度に1つずつ実行するよりも、おそらく多くのことを購入することはありません。