1

Node.Textインデックスに基づいてサブノードを追加しようとするツリービューに問題があります(intインデックスに基づいてこれも試しましたが、役に立ちませんでした)。これは、同期して実行するとうまく機能します。ただし、まったく同じものAsync(backgroundWorker)を実行すると、未処理のArgumentOutOfRange例外がスローされます。もう1つの奇妙な部分は、2つの異なる領域でこの例外をキャッチしようとしたことです。コードを参照してください:

 using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
        {
            int x = 0;
            foreach (string subkey_name in key.GetSubKeyNames())
            {
                using (RegistryKey subkey = key.OpenSubKey(subkey_name))
                {
                    foreach (string a in (string[])subkey.GetValue("Users", ""))
                    {
                        User u = new User(a);
                        usrs.addUser(new User(a));
                        wgs.addUserToWorkgroup(subkey_name, a);
                        usrs.AddWorkGroupToUser(subkey_name, a);
                        int trycount = 0;
                    TryAgain:
                        try
                        {
                            //here is where the exception occurs
                            ExecuteSecure(() => treeView1.Nodes[subkey_name].Nodes.Add(a, a));
                        }
                        catch (ArgumentOutOfRangeException)//This does not catch it.
                        {
                            trycount++;
                            if (trycount < 100)
                            {
                                goto TryAgain; //b/c I cannot catch it this never happens...
                            }
                        }

                    }
                }

                x++;
                //System.Threading.Thread.Sleep(2);
                //As you can see I've tried to let the tread sleep to resolve this 
                //- it will get a little farther but still eventually bomb out.
            }
        }

これがExecuteSecureコードです(https://stackoverflow.com/a/8021020/1387186)

 private void ExecuteSecure(Action a)
    {
        o = new object();
        try
        {
            if (InvokeRequired)
            {
                lock (o)
                {
                    BeginInvoke(a);
                }
            }
            else
                a();
        }
        catch (Exception) //again **sigh** this does not catch the error
        { }
4

3 に答える 3

2

いくつかの問題があります。

  • ループ変数を閉じています。
  • 毎回異なるインスタンスを使用するため、ロックは無意味です。
  • 他のスレッドが競合しないため、ロックは無意味です。
  • を使用する新しいソリューションによりInvoke、UIスレッドとワーカースレッドが前後にピンポンするため、ワーカースレッドが役に立たなくなりました。

ここで私の答えを監視している人なら誰でも、これらのマーシャリング手法について私がどのように感じているかを知っています(のようにInvoke)。これは、UIを更新するための最悪の方法である可能性があります(通常はそうです)。実際、それはあまりにも酷使されているので、カーゴカルトプログラミングの一形態と見なすことができるようになっています。

あなたがすべきことは、ワーカースレッドに更新に必要なデータをTreeViewキューに公開させてから、UIスレッドSystem.Windows.Forms.Timerにあなたに最適な間隔でそれをプルさせることです。これがどのようになるかです。

public class YourForm : Form
{
  private sealed class YourData
  {
    public string SubKeyName { get; set; }
    public string Value { get; set; }
  }

  private ConcurrentQueue<YourData> queue = new ConcurrentQueue<YourData>();

  private void StartTreeViewUpdate_Click(object sender, EventArgs args)
  {
    Task.Factory.StartNew(WorkerThread);
    TreeViewUpdateTimer.Enabled = true;
  }

  private void TreeViewUpdateTimer_Tick(object sender, EventArgs args)
  {
    // Update in batches of 100 (or whatever) so that the UI stays
    // responsive.
    treeView1.BeginUpdate();
    for (int i = 0; i < 100; i++)
    {
      YourData value = null;
      if (queue.TryDequeue(value) && value != null)
      {
        treeView1.Nodes[value.SubKeyName].Nodes.Add(value.Value, value.Value)
      }
      else
      {
        // We're done.
        TreeViewUpdateTimer.Enabled = false;
        break;
      }
    }
    treeView1.EndUpdate();
  }

  private void WorkerThread()
  {
    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
    {
      foreach (string subkey_name in key.GetSubKeyNames())
      {
        using (RegistryKey subkey = key.OpenSubKey(subkey_name))
        {
          foreach (string a in (string[])subkey.GetValue("Users", ""))
          {
            User u = new User(a);
            usrs.addUser(new User(a));
            wgs.addUserToWorkgroup(subkey_name, a);
            usrs.AddWorkGroupToUser(subkey_name, a);
            var data = new YourData();
            data.SubKeyName = subkey_name;
            data.Value = a;
            queue.Enqueue(data);
          }
          queue.Enqueue(null); // Indicate that queueing is done.
        }
      }
    }
  }
}
于 2012-05-25T17:24:31.353 に答える
0

コメントが明らかな場合は、次のようにします。

 var uncapturedSubKey_name = subkey_name;
 ExecuteSecure(() => treeView1.Nodes[uncapturedSubKey_name].Nodes.Add(a, a));

ラムダは他の変数をキャプチャしており、ループが完了するまで実行されません(つまり、最後のsubkey_name .. ..

于 2012-05-25T13:57:11.907 に答える
0

私はこれを次のように解決することができました:

BeginInvoke()をInvoke()に変更することで、Oのスコープも変更して、実際にロックするようにしました。

private void ExecuteSecure(Action a)
{

    try
    {
        if (InvokeRequired)
        {
            lock (o)
            {
                Invoke(a);
            }
        }
        else
            a();
    }
    catch (Exception) //again **sigh** this does not catch the error
    { }
于 2012-05-25T14:30:49.347 に答える