1

複数のスレッドからハッシュセットに追加しようとしています。アイテムがすでに存在する場合は更新し、存在しない場合はリストに追加します。

私が使用しているコードでは、多くの重複が発生していると思います。複数のアイテムが突然同じ参照を指しているためだと思います。しかし、これがどこで、なぜ起こっているのかわかりません。

以下は、私が使用しているコードの後に​​、最初に問題が発生したときに終了する「Log」文字列です。突然、すでに追加されているすべてのアイテムが同じ値になっていることがわかります。

lock (_remoteDevicesLock)
{
    RemoteDevice rDevice = new RemoteDevice(notifyMessage.UUID, notifyMessage.Location);
    log += notifyMessage.UUID + " " + rDevice.UUID;
    if (!_remoteDevices.Add(rDevice))
    {
        log += " Not Added \r\n";
        rDevice = (from d in _remoteDevices
                   where d.UUID.Trim().Equals(notifyMessage.UUID.Trim(), StringComparison.OrdinalIgnoreCase)
                   select d).FirstOrDefault();
        if (rDevice != null)
        {
            //Update Device Expire Time
        }
    }                            
    else
    {
        log += " Added \r\n Current HashSet: \r\n";

        foreach (RemoteDevice rd in _remoteDevices)
        {
            log += rd.UUID + " \r\n";
        }
    }
}


00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Added 

Current HashSet: 
00000000-0000-0001-1000-001cdf885737 

00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Added 

Current HashSet: 
00000000-0000-0001-1000-001cdf885737 
00000000-0000-0001-0002-001cdf885737 

00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Added 

Current HashSet: 
00000000-0000-0001-1000-001cdf885737 
00000000-0000-0001-0002-001cdf885737 
00000000-0000-0001-0001-001cdf885737 

00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Added 

Current HashSet: 
00000000-0000-0001-1000-001cdf885737 
00000000-0000-0001-0002-001cdf885737 
00000000-0000-0001-0001-001cdf885737 
00000000-0000-0001-0000-001cdf885737 

00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737 Not Added 
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737 Added 

Current HashSet: 
00000000-0000-0001-0000-001cdf885737 
00000000-0000-0001-0000-001cdf885737 
00000000-0000-0001-0000-001cdf885737 
00000000-0000-0001-0000-001cdf885737 
00000000-0000-0001-1000-001cdf885737 

更新:ここにGetHashCodeとEquals As Requestedがありますが、手動チェック付きのリストを使用していて問題があったため、問題はここにあるとは思いません。

public override bool Equals(object obj)
{
    var other = obj as RemoteDevice;
    if (other == null)
    {
        return false;
    }
    else
    {
        return UUID.Trim().Equals(other.UUID.Trim(), StringComparison.OrdinalIgnoreCase);
    }
}

public override int GetHashCode()
{
    return UUID.GetHashCode();
}
4

3 に答える 3

2

次の 2 つのUUIDsハッシュ コードは異なりますが、比較すると等しいと見なされます: "x"、" x "。理由: 空白の扱いが異なります。

作成して同意する必要がありGetHashCodeますEquals。true を返す場合Equals、2 つのハッシュ コードは同一である必要があります。この契約を順守しない場合HashSet、未定義の方法で動作します (重複する可能性があります)。

解決策:Trim両方の場所で行うか、またはまったく行わないかのいずれかです。

于 2012-11-21T19:09:56.453 に答える
1

設計上、セット内のアイテムのハッシュ コードは、セット内にある間は決して偶然ではないことが重要です。セットには、オブジェクトの内部状態が変化したことを検出する方法がないため、古いハッシュ値の「バケット」にあるため、新しいハッシュ コードで別のアイテムを追加しようとすると、「バケット」を空にし、アイテムを追加します。現在セットにある項目を「変更」したい場合は、それを削除してから変更してから、再度追加する必要があります。または、(設計の観点から) 古い値を削除して、新しいオブジェクトを追加することをお勧めします。完全に(おそらく、削除されたものからコピーされた特定の側面があります)。

それがあなたの問題だったようです。残りの提案は、あなたが遭遇した問題ではありませんが、以下に残します.

あなたのRemoteDeviceクラスはおそらくオーバーライドせずEqualsGetHashCode意味のある実装をしています。デフォルトの実装(で定義されobjectているのは、オブジェクトのメモリ内のアドレスのみに基づいているため、すべて同じ値を持つ の 2 つの異なるインスタンスは、その定義によって「等しくない」ことになります。一意の ID (GUID には適切な定義がEqualsありGetHashCodeます) 実装はそれに従う必要があります。

すなわち:

public class RemoteDevice
{
    public Guid UUID { get; set; }

    public override bool Equals(object obj)
    {
        RemoteDevice other = obj as RemoteDevice;
        if (other == null) return false;
        return UUID.Equals(other.UUID);
    }

    public override int GetHashCode()
    {
        return UUID.GetHashCode();
    }
}

また、仕組みを誤解しているようにも見えますlock。を使用lock(myObject)しても、他のオブジェクトが を使用できなくなるわけではありませんmyObject。それが行うのは、同じインスタンスにアクセスしようとしている他の誰かが、自分のインスタンスに入る前にlockあなたが終了するまで待機させることだけです。lockこれは、lock誰かがアクセスする前に、コードがオブジェクトの同じインスタンス上にある必要があることを意味しますHashSet(HashSet は複数のスレッドで使用されるように設計されていないため)。

それができない場合、または望ましくない場合は、複数のスレッドからアクセスできるコレクションの作成を検討する必要があります。多くのコレクションには が実装されていますがSystem.Collections.Concurrent、残念ながら はありませんConcurrentSet。いくつかのオプションがあります。独自のものを作成することもできますが、別のオプションとして、 を使用しConcurrentDictionaryて値を無視し、キーのみを使用することもできます。少し面倒ですが、独自の並行コレクションを効果的に作成するのは...難しいです。個人的には、それを使用したい場合は、ConcurrentDictionaryペアを格納するという事実を隠すラッパーを作成するだけです。

于 2012-11-21T18:54:53.200 に答える