需要帮助C#ID管理器在异步运行时无法正常工作

迪恩·朗兹

我已经为xml存储库编写了id管理器。存储库管理xml文件中的条目,并为添加的每个条目分配唯一的ID(整数)。数据库以相同的方式自动将新ID分配给添加到表中的条目。

该存储库将被异步调用,因此我需要id管理器是线程安全的。我正在使用C#lock语句,但似乎无济于事。我的单元测试在单线程执行中成功,但是在并行运行时(IE:Task)失败。具体而言,它们只会在大型并行任务超过1000+时失败,即使如此,它们也只会每隔两次失败。

该异常表明它预期为10000,但得到了9998。该异常始终与2个未注册的丢失ID有关。

我到底想念什么?

下面提供了ID Manager代码和单元测试。id管理器利用Linq,因此对于大量ID并不是很注重性能。单元测试TestAsyncRegistration和TestAsyncRandomRegistration是引发异常的测试。

public class IdManager
{
    private List<int> idList = new List<int>();
    private List<int> availableList = new List<int>();

    private int nextId;

    private int bufferCount;
    object obj = new object();

    public ReadOnlyCollection<int> RegisteredIds
    {
        get
        {
            return new ReadOnlyCollection<int>(this.idList);
        }
    }

    public int BufferCount
    {
        get
        {
            return this.bufferCount;
        }
        set
        {
            if (value < 1)
            {
                throw new ArgumentOutOfRangeException("value");
            }

            this.bufferCount = value;
        }
    }

    public IdManager(int bufferCount)
    {
        this.BufferCount = bufferCount;
        this.Reset();
    }

    public IdManager()
        : this(1000)
    {
    }

    public void RegisterId(int id)
    {
        this.RegisterId(new[] { id });
    }

    public void Reset()
    {
        lock (this.obj)
        {
            this.availableList.Clear();
            this.idList.Clear();
            for (var i = 0; i < this.bufferCount; i++)
            {
                this.availableList.Add(i);
            } 
        }
    }

    public void RegisterId(IEnumerable<int> ids)
    {
        lock (this.obj)
        {
            var distinct = ids.Except(this.idList);
            this.idList.AddRange(distinct);
            this.availableList = this.availableList.Except(this.idList).ToList(); 
        }
    }

    public int NewId()
    {
        lock (this.obj)
        {
            if (this.availableList.Count > 0)
            {
                var item = this.availableList[0];
                this.availableList.RemoveAt(0);
                this.idList.Add(item);
                return item;
            }

            var max = this.idList.Max();

            for (var i = 1; i < this.bufferCount; i++)
            {
                this.availableList.Add(max + i);
            }

            this.availableList = this.availableList.Except(this.idList).ToList();

            return this.NewId(); 
        }
    }
}

...以及单元测试代码...

[TestClass]
public class IdManagerTests
{
    [TestMethod]
    public void TestSequence()
    {
        var manager = new IdManager(5);
        for (var i = 0; i < manager.BufferCount + 10; i++)
        {
            Assert.AreEqual(i, manager.NewId());
        }
    }

    [TestMethod]
    public void TestBrokenSequence()
    {
        var manager = new IdManager(5);
        manager.RegisterId(1);
        Assert.AreEqual(0, manager.NewId());
        Assert.AreEqual(2, manager.NewId());
        for (var i = 3; i < manager.BufferCount + 10; i++)
        {
            Assert.AreEqual(i, manager.NewId());
        }
    }

    [TestMethod]
    public void TestForwardSequence()
    {
        var manager = new IdManager(5);
        manager.RegisterId(0);
        manager.RegisterId(1);
        manager.RegisterId(2);
        Assert.AreEqual(3, manager.NewId());
        Assert.AreEqual(4, manager.NewId());
        for (var i = 5; i < manager.BufferCount + 10; i++)
        {
            Assert.AreEqual(i, manager.NewId());
        }
    }

    [TestMethod]
    public void TestBackwardSequence()
    {
        var manager = new IdManager(5);
        manager.RegisterId(2);
        manager.RegisterId(1);
        manager.RegisterId(0);
        Assert.AreEqual(3, manager.NewId());
        Assert.AreEqual(4, manager.NewId());
        for (var i = 5; i < manager.BufferCount + 10; i++)
        {
            Assert.AreEqual(i, manager.NewId());
        }
    }

    [TestMethod]
    public async Task TestLargeNumbersRegistration()
    {
        // register a list of id's from 0 to 1000
        var list = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i);
        }

        var manager = new IdManager(1000);
        manager.RegisterId(list);

        var taskCount = 10000;
        var taskList = new Task[taskCount];
        var idValue = 0;
        for (int i = 0; i < taskList.Length; i++)
        {
            manager.RegisterId(idValue++);
        }

        Assert.AreEqual(taskCount, manager.NewId());
    }

    [TestMethod]
    public async Task TestAsyncRegistration()
    {
        // register a list of id's from 0 to 1000
        var list = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i);
        }

        var manager = new IdManager(1000);
        manager.RegisterId(list);

        var taskCount = 10000;
        var taskList = new Task[taskCount];
        var idValue = 0;
        for (int i = 0; i < taskList.Length; i++)
        {
            taskList[i] = Task.Factory.StartNew(() => manager.RegisterId(idValue++));
        }

        Task.WaitAll(taskList);

        Assert.AreEqual(taskCount, manager.NewId());
    }

    [TestMethod]
    public async Task TestAsyncRandomRegistration()
    {
        // register a list of id's from 0 to 1000
        var list = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i);
        }

        // randomize the order of the id's in the list
        var random = new Random((int)DateTime.Now.Ticks);
        var randomizedList = from item in list
                             orderby random.Next()
                             select item;

        var manager = new IdManager(1000);
        manager.RegisterId(randomizedList);

        var taskCount = 10000;
        var taskList = new Task[taskCount];
        var idValue = 0;
        for (int i = 0; i < taskList.Length; i++)
        {
            taskList[i] = Task.Factory.StartNew(() => manager.RegisterId(idValue++));
        }

        Task.WaitAll(taskList);

        Assert.AreEqual(taskCount, manager.NewId());
    }
}
务实的

您的问题出在测试中,而不是测试中的方法,特别是代码段:

Task.Factory.StartNew(() => manager.RegisterId(idValue++));

您正在idValue++同时从多个不同的线程进行调用这不是安全的操作。可以在已增加的值idValue之外StartNew传递并传入已增加的值,也可以使用Interlocked.Increment它来安全地对其进行处理。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

位置管理器无法正常工作

来自分类Dev

当Tomcat通过Apache Server运行时,无法访问Tomcat 8管理器

来自分类Dev

Crossrider.com-以管理员身份运行时,IE加载项无法正常工作?

来自分类Dev

Flask应用程序记录器在Gunicorn中运行时无法正常工作

来自分类Dev

jQuery动画在首次运行时无法正常工作

来自分类Dev

正常运行时间脚本帮助

来自分类Dev

位置管理器stopMonitoringForRegion无法正常工作

来自分类Dev

IOS 8中的位置管理器无法正常工作

来自分类Dev

过渡管理器-滑出视图无法正常工作

来自分类Dev

Android-警报管理器无法正常工作?

来自分类Dev

桌面管理器compiz无法正常工作

来自分类Dev

禁用网络管理器后,网络无法正常工作

来自分类Dev

在没有运行窗口管理器的情况下运行时关闭屏幕

来自分类Dev

为什么通过批处理文件使用命令start启动的程序在通过Windows资源管理器运行时无法运行?

来自分类Dev

Mvc运行时错误-尚未启用角色管理器功能

来自分类Dev

在运行时切换窗口管理器

来自分类Dev

通过运行时对象与任务管理器输出的 Java 内存使用情况

来自分类Dev

从外壳程序运行时,Linux“ at”命令工作正常,但从Web服务器运行时,Linux命令失败

来自分类Dev

重复报警管理器无法正常工作,接收器无法启动

来自分类Dev

重复报警管理器无法正常工作,接收器无法启动

来自分类Dev

需要帮助调试缓慢的scons运行时

来自分类Dev

如果计算机正常运行时间大于5分钟,则需要帮助进行bash检查

来自分类Dev

在服务器上运行时,SFTP无法正常运行,但通过驱动程序运行时,SFTP将正常运行

来自分类Dev

从VS 2015 Task Runner资源管理器运行时,Gulp任务失败,但从命令提示符下运行时失败

来自分类Dev

在Service Fabric应用程序中运行时,WebJob SDK无法正常工作

来自分类Dev

当作为登录脚本运行时,PowerShell中的cmdkey无法正常工作?

来自分类Dev

PrimeFaces 5推送:大气运行时本机无法正常工作

来自分类Dev

ARM内联汇编代码在运行时无法正常工作

来自分类Dev

在apache上运行时,Django用户的user.get_profile()无法正常工作

Related 相关文章

  1. 1

    位置管理器无法正常工作

  2. 2

    当Tomcat通过Apache Server运行时,无法访问Tomcat 8管理器

  3. 3

    Crossrider.com-以管理员身份运行时,IE加载项无法正常工作?

  4. 4

    Flask应用程序记录器在Gunicorn中运行时无法正常工作

  5. 5

    jQuery动画在首次运行时无法正常工作

  6. 6

    正常运行时间脚本帮助

  7. 7

    位置管理器stopMonitoringForRegion无法正常工作

  8. 8

    IOS 8中的位置管理器无法正常工作

  9. 9

    过渡管理器-滑出视图无法正常工作

  10. 10

    Android-警报管理器无法正常工作?

  11. 11

    桌面管理器compiz无法正常工作

  12. 12

    禁用网络管理器后,网络无法正常工作

  13. 13

    在没有运行窗口管理器的情况下运行时关闭屏幕

  14. 14

    为什么通过批处理文件使用命令start启动的程序在通过Windows资源管理器运行时无法运行?

  15. 15

    Mvc运行时错误-尚未启用角色管理器功能

  16. 16

    在运行时切换窗口管理器

  17. 17

    通过运行时对象与任务管理器输出的 Java 内存使用情况

  18. 18

    从外壳程序运行时,Linux“ at”命令工作正常,但从Web服务器运行时,Linux命令失败

  19. 19

    重复报警管理器无法正常工作,接收器无法启动

  20. 20

    重复报警管理器无法正常工作,接收器无法启动

  21. 21

    需要帮助调试缓慢的scons运行时

  22. 22

    如果计算机正常运行时间大于5分钟,则需要帮助进行bash检查

  23. 23

    在服务器上运行时,SFTP无法正常运行,但通过驱动程序运行时,SFTP将正常运行

  24. 24

    从VS 2015 Task Runner资源管理器运行时,Gulp任务失败,但从命令提示符下运行时失败

  25. 25

    在Service Fabric应用程序中运行时,WebJob SDK无法正常工作

  26. 26

    当作为登录脚本运行时,PowerShell中的cmdkey无法正常工作?

  27. 27

    PrimeFaces 5推送:大气运行时本机无法正常工作

  28. 28

    ARM内联汇编代码在运行时无法正常工作

  29. 29

    在apache上运行时,Django用户的user.get_profile()无法正常工作

热门标签

归档