1

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

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

  • 大多数应用程序访问此列表并且不会更改它,但可能会通过它进行枚举。所有这些都发生在 UI 线程上。
  • 线程一将定期查找要从列表中添加和删除的项目。
  • 线程二将枚举列表并使用额外信息更新项目。这必须与线程一同时运行,因为可能需要几秒钟到几小时的时间。

第一个问题是是否有人对此有推荐的策略。

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

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

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

如果我得到 copyList,修改它,保存 mainlist,重新启动我的应用程序,加载 mainlist 并再次查看 copylist,那么更改就存在了。我认为我做错了什么,因为 copylist 似乎仍然指的是 mainlist。

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

public static MDGlobalObjects CacheObjects = new MDGlobalObjects();
4

3 回答 3

1

actly 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();
于 2012-05-01T18:45:13.057 回答
1

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

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);
}
于 2012-05-01T19:51:04.210 回答
1

这是使用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 中,如果您可以设置属性以使整个对象在每次原子写入后保持一致,那么您可以跳过制作副本,只需使用集合中的对象填充属性即可。

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

于 2012-05-01T20:58:00.077 回答