错误添加到HashSet的多个线程 [英] Error Adding To HashSet From Multiple Threads

查看:97
本文介绍了错误添加到HashSet的多个线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想添加到哈希从多个线程设置。如果该项目已经存在我想更新它,如果它不存在我希望将其添加到列表中。

随着code我用我结束了,有很多重复的,我presume因为多个项目突然指向同一个参考。我看不到的地方,或者为什么发生这种情况,虽然。

下面是code我用后跟我登录字符串结尾,当我第一次看到这个问题。你可以看到,突然间所有已添加的项目具有相同的价值。

 锁(_remoteDevicesLock)
{
    远端设备rDevice =新的远端设备(notifyMessage.UUID,notifyMessage.Location);
    登录+ = notifyMessage.UUID ++ rDevice.UUID;
    如果(!_remoteDevices.Add(rDevice))
    {
        日志+ =未添加\ r \ N的;
        rDevice =(从D在_remoteDevices
                   其中,d.UUID.Trim()。等于(notifyMessage.UUID.Trim(),StringComparison.OrdinalIgnoreCase)
                   选择D).FirstOrDefault();
        如果(rDevice!= NULL)
        {
            //更新设备到期时间
        }
    }
    其他
    {
        登录+ =新增\ r \ n实际HashSet的:\ r \ N的;

        的foreach(远端设备RD在_remoteDevices)
        {
            登录+ = rd.UUID +\ r \ N的;
        }
    }
}


00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737添加

当前HashSet的:
00000000-0000-0001-1000-001cdf885737

00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737添加

当前HashSet的:
00000000-0000-0001-1000-001cdf885737
00000000-0000-0001-0002-001cdf885737

00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737添加

当前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未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737添加

当前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未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
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 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
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 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-1000-001cdf885737 00000000-0000-0001-1000-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0002-001cdf885737 00000000-0000-0001-0002-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0001-001cdf885737 00000000-0000-0001-0001-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
00000000-0000-0001-0000-001cdf885737 00000000-0000-0001-0000-001cdf885737未添加
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 00000000-0000-0001-1000-001cdf885737添加

当前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
 

更新:这里是GetHash code和equals的要求,虽然我不认为问题就出在这里,因为我是用一个列表手动检查,也有问题。

 公众覆盖布尔等于(obj对象)
{
    VAR其他= OBJ作为远端设备;
    如果(其他== NULL)
    {
        返回false;
    }
    其他
    {
        返回UUID.Trim()等于(other.UUID.Trim(),StringComparison.OrdinalIgnoreCase)。
    }
}

公众覆盖INT GetHash code()
{
    返回UUID.GetHash code();
}
 

解决方案

在设计上,重要的是,在设置项目的哈希值code从来没有偶然,而这是一套内部。本集有没有办法检测对象的内部状态改变了,所以这将是在斗为它的老散列值,所以当您尝试添加另一个项目与新的哈希值code就会看到它的桶是空的,添加的项目。如果你想改变的项目,而这是目前在设置你应该删除它,改变它,然后,或者添加回去更好,但(从设计的角度来看),删除旧的值,并添加一个新的对象全部(可能带有某些方面从删除了一个拷贝)。

看来,是你的问题;我会离开我下面的建议休息,尽管它们不是你所遇到的问题。

远端设备类可能不会覆盖等于 GetHash code 有意义的实现。默认的实现(在对象定义是仅基于在对象的内存地址,所以两个不同的全部相同的值的情况下,将不等于被这个定义,因为它似乎有一个GUID有效的作为一个唯一的ID(GUID具有显等于 GetHash code 定义),您的实现应该只是推迟了这一点。

即:

 公共类远端设备
{
    公众的Guid UUID {获得;组; }

    公众覆盖布尔等于(obj对象)
    {
        远端设备等= OBJ作为远端设备;
        如果(其他== NULL)返回false;
        返回UUID.Equals(other.UUID);
    }

    公众覆盖INT GetHash code()
    {
        返回UUID.GetHash code();
    }
}
 

另外,看来你误解了如何锁定的作品。使用锁(myObject的)没有prevent从以往任何时候都使用任何其他对象 myObject的。它所做的就是引起别人试图在同一实例锁定它等待,直到你离开你的锁定才可以进入他们的。这意味着你的code需要对对象的同一实例锁定之前有人访问的HashSet (由于HashSet中没有设计由多个线程中使用)。

如果这样做,是不是一种选择,或者是不可取的,你需要看看使得可以从多个线程访问的集合。许多藏品在 System.Collections.Concurrent 的实现,但遗憾的是没有 ConcurrentSet 。有几个选项;我们可以使我们自己一人,但另一种选择是使用 ConcurrentDictionary ,简单地忽略值,只是使用的键。这将是一点点凌乱,但有效地创建自己的并发收集是...很难。就个人而言,如果我想用一个我刚刚创造约 ConcurrentDictionary A的包装,掩盖其对存储的事实。

I'm trying to add to a hash set from multiple threads. If the item already exists I want to update it, if it doesn't exist I want to add it to the list.

With the code I'm using I am ending up with lots of duplicates, I presume because multiple items are suddenly pointing to the same reference. I can't see where or why this is happening though.

Below is the code I'm using followed by the "Log" string ending when I first see the issue. You can see that suddenly all the items already added have the same value.

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 

Update: Here is GetHashCode And Equals As Requested although I don't think the issue lies here as I was using a list with a manual check and also had issues.

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();
}

解决方案

By design, it's important that the hash code of an item in the set never chance while it's inside of the set. The set has no way of detecting that the internal state of the object changed, so it will be in the "bucket" for it's old hash value, so when you try to add another item with the new hash code it will see that it's "bucket" is empty and add the item. If you want to "change" an item while it's currently in the set you should remove it, change it, and then add it back in. Or, better yet (from a design perspective) remove the old value, and add a new object entirely (possibly with certain aspect copied from the removed one).

It appears that was your issue; I'll leave the rest of my suggestions below despite them not being the issue you encountered.

Your RemoteDevice class possibly doesn't override Equals and GetHashCode with meaningful implementations. The default implementations (defined in object are based just on the address in memory of the object, so two different instances of with all of the same values will be "not equal" by that definition. Since it seems you have a single GUID valid as a unique ID (GUID has sensible Equals and GetHashCode definitions) your implementation should just defer to that.

i.e.:

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();
    }
}

It also appears that you misunderstand how lock works. Using lock(myObject) doesn't prevent any other object from ever using myObject. All it does is cause anyone else trying to lock on that same instance it wait until you exit your lock before they can enter theirs. This means that your code needs to lock on the same instance of an object before anyone accesses the HashSet (since HashSet wasn't designed to be used by multiple threads).

If doing that isn't an option, or would be undesirable, you'd need to look at making a collection that can be accessed from multiple threads. Many collections have an implementation in System.Collections.Concurrent, but unfortunately there is no ConcurrentSet. There are several options; we could make our own for one, but another option would be to use the ConcurrentDictionary and simply ignore the values and just use the keys. It would be a tad messy, but creating your own concurrent collection effectively is...hard. Personally, if I wanted to use one I'd just create a wrapper around ConcurrentDictionary that hide the fact that it stores pairs.

这篇关于错误添加到HashSet的多个线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆