首页 新闻 会员 周边

大神帮看下代码,为什么ManualResetEventSlim 没有锁住线程, List中的n为什么会有重复?这是Framework中的bug吗?(我的电脑4核i5 7500)

0
[待解决问题]

using System;

using System.Collections.Generic;

using System.Linq;

using System.Security.Cryptography;

using System.Text;

using System.Threading;

using System.Threading.Tasks;

 

namespace ConsoleApp

{

    class Program

    {

        //static AutoResetEvent mre = new AutoResetEvent(true);

        static ManualResetEventSlim mre = new ManualResetEventSlim(true);

        static List<string> list = new List<string>();

        static void Main(string[] args)

        {

 

            Task[] tasks = new Task[10000];

            for (int i = 0; i < 10000; i++)

            {

                tasks[i] = Task.Factory.StartNew(Test);

                //tasks[i].ContinueWith(t => Set());

            }

            

            Task.WaitAll(tasks);

 

            var q = from t in list

                    group t by t into g

                    where g.Count() >1

                    select new

                    {

                        g.Key,

                        Count = g.Count()

                    };

 

            q.ToList().ForEach((t) => Console.WriteLine(t.Key + "---" + t.Count));

 

            Console.WriteLine("OK");

        }

 

        //static void Set()

        //{

        //    mre.Set();

        //}

 

        static int n = 0;

        static string Test()

        {

            try

            {

                mre.Wait();

                mre.Reset();

                Interlocked.Increment(ref n);

                var s = n.ToString();

                list.Add(s);

                return s;

            }

            finally

            {

                mre.Set();

            }

        }

 

    }

}

徐某人的主页 徐某人 | 初学一级 | 园豆:152
提问于:2018-10-24 12:19
< >
分享
所有回答(2)
0

List<T> 线程不安全 换成BlockingCollection<T>再试试

盆栽二代 | 园豆:206 (菜鸟二级) | 2018-10-24 14:35

不是这个问题,代码里面的是

mre.Wait();和

mre.Reset()不起作用,

理论上Wait是获取互斥对象,Reset()是独占对象, Set()后是释放对象。

感觉是mre.Wait()和mre.Reset()这两句话有问题,可能同时有多个线程Wait()通过。

Wait()运行后Cpu时间片到期,还没有Reset(),这时另外一个线程又Wait()通过。所以锁不了线程。

这样的话ManualResetEventSlim 的正确写法应该怎么写呢

支持(0) 反对(0) 徐某人 | 园豆:152 (初学一级) | 2018-10-24 14:49

@徐某人:
你是想验证ManualResetEventSlim锁线程?
如果是的话,你的代码里有很多问题

1 验证多线程同步的时候,不要用 Interlocked.Increment(ref n),请用普通的n++ 。
Interlocked.Increment(ref n) 这个方法本身就是有原子性的,每次加算n的时候会阻塞其他进程,所以无论你用不用ManualResetEventSlim,理论上list里的n不可能重复

2 那么问题来了,为啥会出现你提问的现象。原因是ManualResetEventSlim没锁住进程,List线程不安全。而且你还会发现,List里的个数还可能并不等于10000

3 根据上面说的, Interlocked.Increment(ref n) 该成 n++, List改成BlockingCollection, 其他不动,再次执行程序, 会得到什么结果呢 ? List的个数等于10000了, 但是还是可能有重复的。我猜这个才是你想要的验证

4 回到你的问题, 为啥ManualResetEventSlim没锁住进程, 原因就是你自己感觉的一样, mre.Set() 到mre.ReSet()之间,你无法控制有几个进程通过了mre.Wait()。

5 想锁住进程(同一时间只有1个线程执行)怎么办? 请用Lock

6 根据上面你的回复,感觉你对ManualResetEventSlim的理解有问题,ManualResetEventSlim不是锁线程(同一时间只有1个线程执行)用的,是用来做线程等待和同步的

7 关于ManualResetEventSlim和Lock的区别 给你个参考网站
https://www.codeproject.com/Articles/1114506/Thread-Synchronization-Lock-ManualResetEvent-AutoR

支持(1) 反对(0) 盆栽二代 | 园豆:206 (菜鸟二级) | 2018-10-24 16:35
0

ManualResetEventSlim还没用过,但是用过ManualResetEvent,别的我就不多说了,就是创建ManualResetEvent实例的时候传false,就达到了我想要的效果。这个ManualResetEventSlim也可以试试 new ManualResetEventSlim(false)

柳城之城 | 园豆:39 (初学一级) | 2020-08-29 14:20
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册