优秀的编程知识分享平台

网站首页 > 技术文章 正文

C# 之门课程系列-17(c#教程)

nanyue 2024-10-25 13:19:15 技术文章 2 ℃

线程

被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。

异步委托

委托是方法的类型 安全的引用。Delegate类 还支持异步的调用方法。在后台,delegate类 会创建一个执行任务的线程。

一个例子

 static int TakeAWhile(int data,int ms)
        {
            Console.WriteLine("开始投票");
            Thread.Sleep(ms);
            Console.WriteLine("完成投票");
            return ++data;
        }
static void Main(string[] args)
        {
            TakeAWhileDelegate d1 = new TakeAWhileDelegate(TakeAWhile);
            IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null);
            while (!ar.IsCompleted)
            {
                Console.WriteLine(".");
                Thread.Sleep(500);
            }
            int result = d1.EndInvoke(ar);
            Console.WriteLine("result:{0}", result);
            Console.ReadKey();
        }
public delegate int TakeAWhileDelegate(int data, int ms);

回调

 static void ArCallback(IAsyncResult ar)
        {
            TakeAWhileDelegate d1 = ar.AsyncState as TakeAWhileDelegate;
            int result = d1.EndInvoke(ar);
            Console.WriteLine("result:{0}", result);
        }
 static void Main(string[] args)
        {
            TakeAWhileDelegate d1 = new TakeAWhileDelegate(TakeAWhile);
            IAsyncResult ar = d1.BeginInvoke(1, 3000,ArCallback,d1);
            Console.ReadKey();
        }

Lamdba委托

 static void Main(string[] args)
        {
            TakeAWhileDelegate d1 = new TakeAWhileDelegate(TakeAWhile);
            d1.BeginInvoke(1, 3000, ar =>
            {
                int result = d1.EndInvoke(ar);
                Console.WriteLine("Result:{0}", result);
            },null);
            Console.ReadKey();
        }

Thread类

using System.Threading;

Thread类的一类构造函数为接受ThreadStart和ParameterizedThreadStart类型的委托参数

ThreadStart委托定义了一个返回类型为void的无参数方法

创建Thread对象后,用Start()方法启动线程

一个例子

 static void Main(string[] args)
        {
            var t1 = new Thread(ThreadMain);
            t1.Start();
            Console.WriteLine("这里是主线程!");
            Console.ReadKey();
        }

        static void ThreadMain()
        {
            Console.Write("启动线程!");
        }

给线程传递数据

 static void Main(string[] args)
        {
            string Name = "张三";
            var t1 = new Thread(ThreadMain);
            t1.Start(Name);
            Console.WriteLine("这里是主线程!");
            Console.ReadKey();
        }

        static void ThreadMain(object o)
        {
            Console.WriteLine(o.ToString());
            Console.Write("启动线程!");
        }

Object可以是任意类型

后台线程

在用Thread类创建线程时,IsBackground默认是false。

 static void ThreadMain()
        {
            Console.WriteLine("启动线程!");
            Thread.Sleep(3000);
            Console.WriteLine("完成线程!");
        }
 static void Main(string[] args)
        {
            var t1 = new Thread(ThreadMain)
            {
                Name = "NewThreadMain",
                IsBackground = false
            };
            t1.Start();
            Console.WriteLine("这里是主线程!");
        }

因为新启的线程上加上了Sleep,这个有利于主线程结束

线程池

.NET Framework的ThreadPool类提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。那么什么是线程池?线程池其实就是一个存放线程对象的“池子(pool)”,他提供了一些基本方法,如:设置pool中最小/最大线程数量、把要执行的方法排入队列等等。ThreadPool是一个静态类,因此可以直接使用,不用创建对象。

微软官网说法如下:许多应用程序创建大量处于睡眠状态,等待事件发生的线程。还有许多线程可能会进入休眠状态,这些线程只是为了定期唤醒以轮询更改或更新的状态信息。 线程池,使您可以通过由系统管理的工作线程池来更有效地使用线程。

线程池

怎么使用线程池? 其实线程池使用起来很简单,如下图

设置线程池最大最小:ThreadPool.SetMaxThreads (int workerThreads,int completionPortThreads)

设置可以同时处于活动状态的线程池的请求数目。所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。还可以设置最小线程数。

将任务添加进线程池:ThreadPool.QueueUserWorkItem(new WaitCallback(方法名));

或ThreadPool.QueueUserWorkItem(new WaitCallback(方法名), 参数);

举个小例子,线程池中最多5个线程,执行一个方法60次,算5年总工资,如下: 如果不采用线程池,恐怕要开60线程异步执行Run()方法,空间资源之浪费,可见一斑。

而现在我们最多用了5个线程,1秒内即可执行完毕,效率、性能都很好。

例子

 static void Main(string[] args)
        {
            int ThreadCount = 0;
            int CompletePortThreads = 0;
            //参数1:workerThreads :线程池中辅助线程的最大数目。
            //参数2:completionPortThreads :线程池中异步 I/ O 线程的最大数目。 
            ThreadPool.GetMaxThreads(out ThreadCount, out CompletePortThreads);
            Console.WriteLine("ThreadCount:{0},CompletePortThreads:{1}"
                , ThreadCount, CompletePortThreads);
            for (int i = 0; i < 5; i++)
            {
                ThreadPool.QueueUserWorkItem(JobAsThread);
            }
            Console.ReadKey();
        }

        static void JobAsThread(object state)
        {
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("loop {0},theadid {1}", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
        }

大家看到的结果可能不一样

任务

.net4包含新名称空间 system.Threading.Tasks,它包含的类抽象出了线程功能。 在后台使用 ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以 以同步方式启动一个任务,这需要等待主调线程。 使用任务不仅可以获得一个抽象层,还可以对 底层线程进行很多控制。

一个例子

 static void Main(string[] args)
        {
            TaskFactory tf = new TaskFactory();
            //这两个顺序是一定的
            Task t1 = tf.StartNew(TaskMethod);
            Task t2 = tf.StartNew(TaskMethod);
            Console.WriteLine("Main");
            Console.ReadKey();
        }

        static void TaskMethod()
        {
            Console.WriteLine("Running in a task");
            Console.WriteLine("Task ID:{0}", Task.CurrentId);
        }

带返回任务

 static void Main(string[] args)
        {
            Task<int> task = CreateTask();
            task.Start();
            int result = task.Result;
            Console.WriteLine(result);
            Console.WriteLine("Main");
            Console.ReadKey();
        }
 static Task<int> CreateTask()
        {
            return new Task<int>(() => ReturnMethod());
        }

        static int ReturnMethod()
        {
            return new Random().Next(1, 99);
        }

Parallel类

Parallel类提供了数据和任务的并行性;

Parallel类是对线程的一个抽象。该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性。Paraller类定义了数据并行地For和ForEach的静态方法,以及任务并行的Invoke的静态方法。Parallel.For()和Parallel.ForEach()方法在每次迭代中调用相同的代码,Paraller.Invoke()允许调用不同的方法。

Parallel.For

Parallel.For()方法类似C#语法的for循环语句,多次执行一个任务。但该方法并行运行迭代,迭代的顺序没有定义。   Parallel.For()方法中,前两个参数定义了循环的开头和结束,第三个参数是一个Action委托。Parallel.For方法返回类型是ParallelLoopResult结构,它提供了循环是否结束的信息。

一个例子

 static void ForTest()
        {
            ParallelLoopResult plr =
                Parallel.For(0, 10, i => {
                    Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(5000);
                });

            if (plr.IsCompleted)
                Console.WriteLine("completed!");

        }

Parallel.ForEach方法遍历实现了IEnumerable的集合,类似于foreach,但以异步方式遍历。没有确定遍历顺序。

 static void ForeachTest()
        {
            string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
            ParallelLoopResult plr = Parallel.ForEach<string>(data, s =>
            {
                Console.WriteLine(s);
            });

            if (plr.IsCompleted)
                Console.WriteLine("completed!");
        }

Timer 类

需要引入 System.Threading;

在很多时间我们都需要进行延迟执行,或是定时执行一些指定业务,这个时候使用 Timer 是最合适的,而且 Timer 是Cpu 级别处理对系统影响很少,就算创建上千上万个 Timer 也不会影响。

一个例子

 //构建 Timer
        //duetime:调用 callback 之前延迟的时间量(以毫秒为单位)。 
        //指定 Infinite 可防止启动计时器。 指定零 (0) 可立即启动计时器。
        static Timer timer = new Timer(TimerCallBack,null, Timeout.Infinite,1000);
        static void Main(string[] args)
        {
            //启动
            timer.Change(0, 1000);
            Console.ReadKey();
        }

        static void TimerCallBack(object state)
        {
            Console.WriteLine("Now:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
        }

Tags:

最近发表
标签列表