4

クリックイベント時にネットワークから情報を取得するボタンがあります。

情報を取得したら、それを解析してリストボックスにアイテムを追加します。すべて問題ありませんが、ボタンをすばやくダブルクリックすると、2人のバックグラウンドワーカーが実行されているようで、すべての作業が終了すると、リスト内のアイテムが複製されます。

ボタンをクリックして情報を取得するプロセスが機能している場合、このスレッドは停止し、最初の作業が完了した後にのみ2番目のスレッドが開始されるようにしたいと思います。
はい、私はAutoResetEventについて知っていますが、それを使用したとき、それは1回だけ、そしてそれ以上は役に立たなかった。私はこの状況を実行することはできません、そしてあなたが私を助けてくれることを願っています!

今でも簡単にしようとしていますが成功しません:(:フラグフィールド(RefreshDialogs)(デ​​フォルトはfalse)を追加しました。ユーザーがボタンをクリックすると、フラグがtrue(作業が行われていることを意味します)の場合、何も行われません。ただし、フラグフィールドがfalseに設定されている場合は、すべて正常であり、新しいプロセスを開始します。Backgroundworkが
完了したら、フィールドフラグをfalseに変更します(ユーザーが新しいプロセスを実行できることを意味します)。

    private void Message_Refresh_Click(object sender, EventArgs e)
    {
        if (!RefreshDialogs)
        {
            RefreshDialogs = true;
            if (threadBackgroundDialogs.WorkerSupportsCancellation)
            {
                threadBackgroundDialogs.CancelAsync();
            }
            if (!threadBackgroundDialogs.IsBusy)
            {
                downloadedDialogs = 0;
                threadBackgroundDialogs = new BackgroundWorker();
                threadBackgroundDialogs.WorkerSupportsCancellation = true;
                threadBackgroundDialogs.DoWork += LoadDialogs;
                threadBackgroundDialogs.RunWorkerCompleted += ProcessCompleted;
                threadBackgroundDialogs.RunWorkerAsync();
            }
        }
    }
    void ProcessCompleted(object sender,  RunWorkerCompletedEventArgs e)
    {
        RefreshDialogs = false;
    }
4

6 に答える 6

2

したがって、最初のプロセスが機能している間、2番目のプロセスを実行し続けたいのですが、お互いに邪魔するべきではありませんか?そして、最初のものが終わった後、2番目のものは続きますか?

粗雑な方法:Whileループ:

        if (!RefreshDialogs)
    {
        RefreshDialogs = true;

これは次のようになります。

    while(RefreshDialogs)
    {
    }
        RefreshDialogs = true;

falseに設定すると、2番目のプロセスがしばらくの間飛び出します。(両方のプロセスが常に実行されるため、これは非常に非効率的であることに注意してください。2番目のプロセスが最初のプロセスをブロックすると確信していますが、マルチタスクでは、Dispatcher.Threadを使用してブロックする場合はブロックしないはずです)

エレガントな方法:セマフォを使用する

http://msdn.microsoft.com/de-de/library/system.threading.semaphore%28v=vs.80%29.aspx

両方のプロセスを同時に実行することが不可能な場合、または別の方法が必要な場合:Array / List / intを追加し、2番目のプロセスがboolのように最初のプロセスが実行されていることに気付いたら、追加された変数を増やします。プロセスの最後に、新しいプロセスを再起動し、変数を減らします。

    int number;

    if (!RefreshDialogs)
    {
        RefreshDialogs = true;
        your code;
        if(number > 0)
        {
          number--;
          restart process
        }
    }
    else
    {
    number++;
    }

非常に効率的であるため、私の最後の提案が最も好きであることを認めなければなりません。

于 2012-08-27T08:56:22.230 に答える
1

スレッドをブロックします。それは簡単だ;

lock(someSharedGlobalObject)
{
  Do Work, Exit early if cancelled
}

このようにして、他のスレッドは最初のスレッドがロックを解放するまで待機します。それらは同時に実行されることはなく、続行できるようになるまで静かに待機します。

他のオプションについては; クリックしたときにボタンを無効にし、backgroundworkerが完了したときにボタンを再度有効にしてみませんか。唯一の問題は、これでは現在のスレッドをキャンセルできないことです。ユーザーはそれが終了するのを待つ必要があります。これにより、並行性が非常に簡単になくなります。

于 2012-08-27T09:07:21.580 に答える
1

このアプローチはどうですか?

ボタンがクリックされるたびにインクリメントされるリクエストキューまたはカウンターを作成します。そのカウントが>0になるたび。バックグラウンドワーカーを起動します。情報が表示されたら、カウントをデクリメントして0を確認します。それでも0より大きい場合は、ワーカーを再起動します。その点で、リクエストハンドラはシーケンシャルになります。

このアプローチでは、2つのスレッドによるカウントの継続的な参照の問題に直面する可能性があります。そのため、ロックロック解除条件を使用する可能性があります。

私は自分のアプリでこのアプローチを採用しましたが、うまく機能します。同じように機能することを願っています。

于 2012-08-27T09:17:18.847 に答える
0

私はWindowsPhoneの専門家ではありませんが、TPLをサポートしているので、次のコードは適切に読み取れます。

private object syncRoot =new object();
private Task latestTask;

public void EnqueueAction(System.Action action)
{
    lock (syncRoot)
    {
        if (latestTask == null)
            latestTask = Task.Factory.StartNew(action);
        else
            latestTask = latestTask.ContinueWith(tsk => action());
    }
}
于 2012-08-27T09:26:47.993 に答える
0

セマフォを使用できます

  class TheClass
 {
    static SemaphoreSlim _sem = new SemaphoreSlim (3);

    static void Main()   
    {     
       for (int i = 1; i <= 5; i++) 
       new Thread (Enter).Start (i);   
    } 

    static void Enter (object name)   
    {     
      Console.WriteLine (name + " wants to enter");    
      _sem.Wait();     
      Console.WriteLine (name + " has entered!");               
      Thread.Sleep (1000 * (int) name );              
      Console.WriteLine (name + " is leaving");       
      _sem.Release();   } 
     }
 }
于 2012-08-27T09:13:06.697 に答える
0

私は解決策を見つけ、@Giedriusに感謝します。フラグRefreshingDialogsは、リストボックスにアイテムを追加したときにプロセスが終了した場合にのみtrueに設定されます。このフラグを使用している理由は、ネットワークからコンテンツを取得する非同期操作(HttpWebRequest、メソッドBeginGetRequestStream)が開始されると、プロセスの状態が完了に変わるためですが、ネットワーク操作が完了した後、UI操作を行う必要があります。 (コンテンツを解析してリストボックスに追加します)私の解決策は次のとおりです。

    private object syncRoot = new object();
    private Task latestTask;

    public void EnqueueAction(System.Action action)
    {
        lock (syncRoot)
        {
            if (latestTask == null)
            {
                downloadedDialogs = 0;
                latestTask = Task.Factory.StartNew(action);
            }
            else if(latestTask.IsCompleted && !RefreshingDialogs)
            {
                RefreshingDialogs = true;
                downloadedDialogs = 0;
                latestTask = Task.Factory.StartNew(action);
            }
        }
    }

    private void Message_Refresh_Click(object sender, EventArgs e)
    {
        Action ac = new Action(LoadDialogs2);
        EnqueueAction(ac);
    }
于 2012-08-27T12:15:25.917 に答える