列出线程问题

本文关键字:问题 线程 | 更新日期: 2023-09-27 18:32:46

我正在尝试使我的应用程序线程安全。 我举起双手,承认我是线程新手,所以不知道该怎么做。

为了给出简化版本,我的应用程序包含一个列表。

  • 大多数应用程序访问此列表并且不会更改它,但是可以通过它枚举。所有这些都发生在 UI 线程上。
  • 线一个人将定期查找要从列表。
  • 线程 2 将枚举列表并更新项目额外信息。 这必须与线程 1 同时运行可能需要从几秒钟到几小时的任何时间。

第一个问题是,有没有人对此有推荐的流浪者。

其次,我试图制作主应用程序将使用的列表的单独副本,在更新/添加或删除某些内容时定期获取新副本,但这似乎不起作用。

我有我的清单和一份副本...

public class MDGlobalObjects
{
    public List<T> mainList= new List<T>();
    public List<T> copyList
    {
        get
        {
            return new List<T>(mainList);
        }
    }
}

如果我得到 copyList,修改它,保存主列表,重新启动我的应用程序,加载主列表并再次查看 copylist,然后更改就存在。我想我做错了什么,因为复制列表似乎仍然引用主列表。

我不确定它是否有区别,但所有内容都是通过类的静态实例访问的。

public static MDGlobalObjects CacheObjects = new MDGlobalObjects();

列出线程问题

这是使用 ConcurrentDictionary 的要点:


public class Element
{
    public string Key { get; set; }
    public string Property { get; set; }
    public Element CreateCopy()
    {
        return new Element
        {
            Key = this.Key,
            Property = this.Property,
        };
    }
}

var d = new ConcurrentDictionary<string, Element>();
// thread 1
// prune
foreach ( var kv in d )
{
    if ( kv.Value.Property == "ToBeRemoved" )
    {
        Element dummy = null;
        d.TryRemove( kv.Key, out dummy );
    }
}
// thread 1
// add
Element toBeAdded = new Element();
// set basic properties here
d.TryAdd( toBeAdded.Key, toBeAdded );
// thread 2
// populate element
Element unPopulated = null;
if ( d.TryGetValue( "ToBePopulated", out unPopulated ) )
{
    Element nowPopulated = unPopulated.CreateCopy();
    nowPopulated.Property = "Populated";
    // either
    d.TryUpdate( unPopulated.Key, nowPopulated, unPopulated );
    // or
    d.AddOrUpdate( unPopulated.Key, nowPopulated, ( key, value ) => nowPopulated );
}
// read threads
// enumerate
foreach ( Element element in d.Values )
{
    // do something with each element
}
// read threads
// try to get specific element
Element specific = null;
if ( d.TryGetValue( "SpecificKey", out specific ) )
{
    // do something with specific element
}

在线程 2 中,如果可以设置属性以使整个对象在每次原子写入后保持一致,则可以跳过创建副本,而只需使用集合中的对象填充属性。

此代码中有一些争用条件,但它们应该是良性的,因为读者始终对集合具有一致的视图。

实际上 copylist 只是 mainList 的浅拷贝。 该列表是新的,但列表中包含的对象的引用仍然相同。 要实现您正在努力实现的目标,您必须对列表进行深层复制像这样的东西

public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection) where T : ICloneable
{
    return collection.Select(item => (T)item.Clone());
}

并像使用它一样使用

return mainList.Clone();

再次查看您的问题.. 我想建议整体改变方法。您应该像使用 .Net 4.0 一样使用 ConcurrentDictionary() 。因为您不会将锁用作并发集合始终保持有效状态。
所以你的代码将看起来像这样。

Thread 1s code --- <br>
var object = download_the_object();
dic.TryAdd("SomeUniqueKeyOfTheObject",object);
//try add will return false so implement some sort of retry mechanism
Thread 2s code
foreach(var item in Dictionary)
{
 var object item.Value;
var extraInfo = downloadExtraInfoforObject(object);
//update object by using Update
dictionary.TryUpdate(object.uniqueKey,"somenewobjectWithExtraInfoAdded",object);
}