线程池,千万注意,原来很多人都在错用

[C#线程池] 场景:以下是C# winform写的一个线程池示例程序。窗体上,分别拖放一个开始和停止按钮,单击开始按钮,for寻觅模拟7000个任务队列,在线程池运行过程中,可以点击停止按钮,来结束线程池所有任务,这里借助CancellationTokenSource对象,来实现线程池的停止。 &

[C#线程池]

场景:以下是C# winform写的一个线程池示例程序。窗体上,分别拖放一个开始和停止按钮,单击开始按钮,for寻觅模拟7000个任务队列,在线程池运行过程中,可以点击停止按钮,来结束线程池所有任务,这里借助CancellationTokenSource对象,来实现线程池的停止。

多线程

开始前,请大家看以下代码,有什么问题?这里以C# Winform的形式,向大家展现以下代码:

1、第一种:这是开始按钮单击事件的代码

            btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
            System.Threading.ThreadPool.SetMinThreads(2, 1);
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

2、第二种:看出有何不同了吗,好像差不多,都很正常对吧

 btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
          
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

3、第三种:

 btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMinThreads(2, 1);
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
            
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

如果没有看出以上三段代码有何不同,那就一定要看看下面的内容。

接下来,我们借助操作系统的任务管理器,来及时监测Winform进程所进行的线程数。

如果,是第一种写法,我们来看看:

结果,并不像我们预想的那样,即使如此,线程数还在一路飙升,不一会儿竟达到两百多个线程,这是为何?

第二种,只不过是比第一种少些了一行System.Threading.ThreadPool.SetMinThreads(2, 1);的代码,结果,

好像没有什么变化,线程池仍然一路飙升,直到UI线程出现假死。

就在我写这两段博客的瞬间,看看任务管理器的情况吧:

什么,线程数601?暂不说它是否精确,但此刻,程序是很不正常的在运行。

第三种,是在第一种基础上,调整了两行代码的顺序,恰是这两行代码,让我们正视了之前线程池误区。也改变了结果。以第三种方式,我将程序运行6个多小时,一夜不关,程序依然运行平稳,线程数依然保持在较稳的水平。

原来,顺序真的很重要。经常看到网上有关C#线程池用法的博客,当我们看到,上来都写

System.Threading.ThreadPool.SetMaxThreads(4, 2);这行代码时,我们切记要注意了。当任务量小的时候,这种问题并不明显,依然能很好的完成工作。但是,像我以上的例子,庞大任务队列加入线程池,每个任务又以漫长的运行周期在运行。那把上面那个例子,搬上去看看,这种写法必死,切记。

使用C#线程池的时候,一定要先设置 System.Threading.ThreadPool.SetMinThreads(2, 1);,然后再设置

System.Threading.ThreadPool.SetMaxThreads(4, 2);,这才是C#线程池正确的用法。

最后给大家介绍一个关键词volatile,不知道的朋友,可以了解下。想要了解更多多线程池方面的信息,请访问http://www.lichaoqiang.com/

李 朝强
版权声明:本站原创文章,于8个月前,由发表,共 3188字。
评论 目前评论:1   访客  1   博主  0
  1. Kayleigh 2017年01月04日 上午4:18 回复
    What a dick-time to insert Reagan’s name, eh Rocketman. Hahahaha, Reagan the retreater and his penchant for killing Central American women and babies. Hey, sometimes you have to kill ‘em women and babies so you can send ‘em missiles to Iran.Boy, those were the days of American diplomatic pride, huh. Commandante Daniel &#i2n0;Sand82ista Maximillo” Ortega! Duly elected president of Nicaragua! Three cheers for democrazy democracy! Where’s Reagan now?
× 提示!评论加载中...
发表评论
邮箱 »
昵称 »
 Flag Counter