c#多线程进阶

一、多线程传递参数的方法
整理自:https://www.cnblogs.com/shi5588/p/6130536.html

1 、带参数的委托方法来传递参数

       static void Main(string[] args)
        {
            var d = new Data { Message = "中国" };
            //通过带参数的委托方法来传递参数 ParameterizedThreadStart
            var t2 = new Thread(ThreadMainWithParameters);
            t2.Start(d); 
            System.Console.ReadLine();
        }
        static void ThreadMainWithParameters(object o) // 此处定义必须使用Object
        {
            Data d = (Data)o;
            Console.WriteLine("Running in a thread, received {0}", d.Message);
        }

2 通过成员方法和带参数的构造函数传参数

  class Program
    {
        static void Main(string[] args)
        {
            var obj = new MyThread("广东");
            var t3 = new Thread(obj.ThreadMain);
            t3.Start();
            Console.ReadLine();
        }
   }
    public class MyThread                                                                                                                                                                                                                                                                                                                                                                                                                             
    {
        private string data;
        public MyThread(string data)
        {
            this.data = data;
        }
        /// <summary>
        /// 线程方法
        /// </summary>
        public void ThreadMain()
        {
            Console.WriteLine("Running in a thread, data: {0}", data);
        }
    }
    public struct Data
    {
        public string Message;
    }

二、前台线程和后台线程

1.默认情况下,你自己显示创建的线程是前台线程。前台线程保持这个应用程序一直存活只要其中任意一个正在运行,而后台线程不是这样的。一旦所有的前台线程完成,这个应用程序就结束了,
2.任何正在运行的后台线程立刻终止。 一个线程前台/后台的状态跟它的优先级和配置的执行时间没有关联。
3.你可以使用线程的IsBackgroud属性查询或改变一个线程的后台状态。
4.程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作。

前台线程和后台线程的区别和联系:
1、后台线程不会阻止进程的终止。属于某个进程的所有前台线程都终止后,该进程就会被终止。所有剩余的后台线程都会停止且不会完成。
2、可以在任何时候将前台线程修改为后台线程,方式是设置Thread.IsBackground 属性。
3、不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。
4、托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程。

static void Main(string[] args)
{
    Thread t = new Thread(() => Console.ReadKey());
    if (args.Length > 0)//如果Main方法没有传入参数
    {
        //设置线程为后台线程,等待用户输入。
        //因为主线程在t.Start()执行之后就会终止,
        //所以后台线程t会在主线程退出之后,立即终止,应用程序就会结束。
        t.IsBackground = true;
    }
    t.Start();
}

三、线程优先级问题

整理自:http://www.cnblogs.com/jackson0714/p/5103898.html#autoid-5-0-0
一个线程的优先级决定了在操作系统中它可以得到多少相对其他线程的执行时间,下面是线程优先级的等级:

// Summary:
// Specifies the scheduling priority of a System.Threading.Thread.
[Serializable]
[ComVisible(true)]
public enum ThreadPriority
{
    Lowest = 0,
    BelowNormal = 1,
    Normal = 2,
    AboveNormal = 3,
    Highest = 4,
}

当多线程同时是激活的,线程优先级是很重要的。
注意:提高线程优先级时,需要非常小心,这将可能导致其他线程对资源访问的饥饿状态的问题。
当提升一个线程的优先级时,不会使它执行实时工作,因为它被应用程序的进程优先级限制了。为了执行实时工作,你也必须通过使用System.Diagnostices的Process类来提升进程的优先级:

using (Process p = Process.GetCurrentProcess())
{
    p.PriorityClass = ProcessPriorityClass.High;
}

ProcessPriorityClass.High事实上是优先级最高的一档:实时。设置一个进程优先级到实时状态将会导致其他线程无法获得CPU时间片。如果你的应用程序意外地进入一个无限循环的状态,你甚至会发现操作被锁住了,只有电源键能够拯救你了。针对这个原因,High通常对于实时应用程序是最好的选择。

如果你的实时应用程序有一个用户界面,提高程序的优先级将会使刷新界面占用昂贵的CPU的时间,且会使整个系统变得运行缓慢(尤其是UI很复杂的时候)。降低主线程优先级且提升进程的优先级来确保实时线程不会被界面重绘所抢占,但是不会解决其他进程对CPU访问缺乏的问题,因为操作系统整体上会一直分配不成比例的资源给进程。一个理想的解决方案是让实时线程和用户界面用不同的优先级运行在不同的进程中,通过远程和内存映射文件来通信。即使提高了进程优先级,在托管环境中处理硬实时系统需求还是对适用性有限制。此外,潜藏的问题会被自动垃圾回收引进,操作系统会遇到新的挑战,即使是非托管代码,使用专用硬件或者特殊的实时平台,那将被最好的解决。

四、线程池

1、线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务
2、每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。
3、如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
4、创建太多的线程是有限制的,在这种情况下新的操作将在队列中等待直到线程池中的工作线程有能力来执行它们。
5、当停止向线程池中放置新操作时,线程池最终会删除一定时间后过期的不再使用的线程。这将释放所有那些不再需要的系统资源。
6、线程池的用途是执行运行时间短的操作。使用线程池可以减少并行度耗费,及节省操作系统资源。
我们只使用较少的线程,但是以比平常更慢的速度来执行异步操作, ,使用一定数量的可用的工作线程批量处理这些操作。如果操作能快速地完成则比较适用线程!池,但是执行长时间运行的计算密集型操作则会降低性能。

当使用线程池时需要注意下面的事情:

1、你不能设置一个线程的名字,因为设置线程的名字将会使调试更困难(当你在VS线程窗口中调试时,即使你可以附加一个描述)。
2、线程池中的线程总是后台线程(这通常不是问题)。 3、在应用程序的开始期间,阻塞一个线程可能会触发一个延迟,除非你调用ThreadPool.SetMinThreads
4、你不能任意地改变池中的线程的优先级-因为当它释放会池中的时候,优先级会被还原为正常状态。
5、你可以通过属性Thread.CurrentThread.IsThreadPoolThread的属性查询线程是否是正在运行的一个池中的线程

ThreadPool.SetMinThreads:
task默认对线程的调度是逐步增加的,连续多次运行并发线程,会提高占用的线程数,而等若干秒不运行,线程数又会降低。这样,会影响程序多次运行的效率。
即使使用了TaskCreationOptions.LongRunning参数,依然效率偏低。对于一些固定执行时间的线程,我们可以提高线程池的最小线程数,来显著提高task多线程的效率。
ThreadPool.SetMinThreads(100, 100);
提高最小线程数之后,可以不使用LongRunning参数。

未完待续。。。

猜你喜欢

转载自blog.csdn.net/qq_41885871/article/details/83823054