0

我有以下情况:

一个单例类,我们称它为 Coordinator,它有多个线程正在加入。该类的作用是以线程安全的方式“初始化”某个实体(EntityObject)。假设我们有 5 个线程试图初始化一个 EntityObject。应该只允许一个线程来初始化 EntityObject,而其他 4 个线程应该等到初始化完成。EntityObject 的名称是唯一的。

下面是一些代码来说明这一点:

public class EntityObject
{
    public EntityObject()
    {
        IsInitialized = false;
        Name = string.Empty;
    }

    public bool IsInitialized { get; set; }

    public string Name { get; set; }
}

public class InitializeArguments
{
    public EntityObject Entity { get; set; }
}

 public class Coordinator
{
    public void initialize(InitializeArguments args)
    {
        if (!args.Entity.IsInitialized)
        {
            //initializeCode goes here
            //only one thread is allowed to initialize an EntityObject with a certain Name
            //the other threads have to wait until initialization is done
            args.Entity.IsInitialized = true;
        }
    }
}


 class Program
{
    static void Main(string[] args)
    {
        List<Task> allTask = new List<Task>();

        Coordinator coordinator = new Coordinator();

        EntityObject entity1 = new EntityObject() { IsInitialized = false, Name = "entity1" };
        EntityObject entity2 = new EntityObject() { IsInitialized = false, Name = "entity2" };
        EntityObject entity3 = new EntityObject() { IsInitialized = false, Name = "entity3" };

        for (int i = 0; i < 4; i++)
        {
            var task = Task.Factory.StartNew(() =>
             {
                 InitializeArguments initArg = new InitializeArguments() { Entity = entity1 };
                 coordinator.initialize(initArg);
             });
            allTask.Add(task);
        }

        for (int i = 0; i < 4; i++)
        {
            var task = Task.Factory.StartNew(() =>
            {
                InitializeArguments initArg = new InitializeArguments() { Entity = entity2 };
                coordinator.initialize(initArg);
            });
            allTask.Add(task);
        }

        for (int i = 0; i < 4; i++)
        {
            var task = Task.Factory.StartNew(() =>
            {
                InitializeArguments initArg = new InitializeArguments() { Entity = entity3 };
                coordinator.initialize(initArg);
            });
            allTask.Add(task);
        }

        Task.WaitAll(allTask.ToArray());
        Console.ReadLine();
    }
}

在这种情况下,entity1,entity2,entity3 应该只初始化一次。

我正在考虑使用 a Dictionary<string, ManualResetEventSlim>来实现这一点,但我无法让它发挥作用。

4

3 回答 3

3

获取一个,只允许一个线程进入临界区:

public class Coordinator
{
    private static object lockObj = new Object();

    public void initialize(InitializeArguments args)
    {
        lock(lockObj)
        {
            if (!args.Entity.IsInitialized)
            {
            ...
            }
        }
    }
}

考虑您的评论:您可以锁定实体本身,但这被认为是不好的做法(请参阅上面的链接并记住这篇文章):

通常,避免锁定公共类型或超出代码控制范围的实例。常见的构造 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反了本指南

所以也许你没问题

lock(args.Entity)

而不是lock(lockObj)......但是不要锁定实体的名称——也许它在您的控制范围内是唯一的,但谁保证它在整个过程中是唯一的?

于 2013-03-12T15:53:22.727 回答
1

如何使用工厂并像这样实现双重锁定模式:

public class EntityFactory
{

    private static Dictionary<string, EntityObject> _entityObjects = new Dictionary<string, EntityObject>();
    private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

    public EntityObject CreateEntity(string name)
    {
        EntityObject result = null;

        try
        {

            if (!_entityObjects.TryGetValue(name, out result))
            {
                Lock.EnterWriteLock();
                try
                {
                    if (!_entityObjects.TryGetValue(name, out result))
                    {
                        // initialisation code here
                        result = new EntityObject() {Name = name};
                        _entityObjects[name] = result;
                    }
                }
                finally
                {
                    Lock.ExitWriteLock();
                }
            }
        }
        finally
        {
            Lock.ExitUpgradeableReadLock();
        }

        return result;

    }
}

工厂应该是单例的。它有一个实体对象的静态字典(按名称键)。它检查实体对象是否存在,如果存在则返回,否则获取写锁,然后再次检查字典。因为在维护写锁的同时,另一个线程可能已经创建了一个。如果字典中仍然没有它会创建一个,请将我在其中添加注释的代码替换为您的 EntityObject 初始化代码。然后它将实体对象存储在字典中并返回它。

于 2013-03-12T15:57:53.037 回答
0

修改@KevinHolditch 的代码以缓存对象创建过程与实际对象。摆脱了,Lock.ExitUpgradeableReadLock();因为它因为没有读锁而产生错误。除非明确要求引用相等,否则假设EntityObject实现应该没有问题。IEquatable

public class EntityFactory
{
    private static Dictionary<string, Func<EntityObject>> _entityObjects = new Dictionary<string, Func<EntityObject>>();
    private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

    public EntityObject CreateEntity(string name)
    {
        Func<EntityObject> result = () => null;

        if (!_entityObjects.TryGetValue(name, out result))
        {
            Lock.EnterWriteLock();
            try
            {
                if (!_entityObjects.TryGetValue(name, out result))
                {
                    result = () =>
                        {
                            // initialisation code here
                            var entity = new EntityObject { Name = name };
                            return entity;
                        };
                    _entityObjects[name] = result;
                }
            }
            finally
            {
                Lock.ExitWriteLock();
            }
        }

        return result();
    }
}
于 2013-03-12T16:20:52.173 回答