(17)多线程处理

**线程using System.Threading.Tasks;

//简单的线程实例

            const int reprtitions = 100;
            Task task = new Task(() =>
            {
                for (int count = 0; count < reprtitions; count++)
                {
                    Console.Write(count);
                    
                }
            });
            //
            //线程开始
            task.Start();
            Console.WriteLine(task.Status.ToString());
            for (int count = 0; count < reprtitions; count++)
            {
                char a ='a';
                Console.Write((a++).ToString());
            }
            Console.WriteLine(task.Status.ToString());
            //线程等待
            task.Wait();
            Console.WriteLine(task.Status.ToString());
            Console.ReadKey();


**Task的一些属性

//用工厂方法创建和开始新的Task

 Task<string> task = Task.Factory.StartNew<string>();

1.Status;

2.IsCompleted;

3.Id;

4.AsyncState;能跟踪额外的数据,假如多任务压迫计算一个List<T>中的值,为了将结果放发列表中的正确位置,一个办法是将准备包含结果的那个列表索引存储到AsyncState属性中,这样一来,代码可以使用AsyncState(先转型到Int)访问列表中的特定索引位置。List<T>.Add()不是一个跨多线程的安全操作,可能造成竞争条件,导致数据丢失。

5.Task.CurrentId   (static 属性) 它返回正在执行的Task的那个标示符。

**ContinueWith():是将任务连接起来。只要链中先驱任务(antecedent task)完成后立即开始在它之后的其他任务。

如果在同一个任务上调用多次ContinueWith(),该任务完成后,添加的多个任务会并行运行。

需要“登记”先驱任务行为的“通知:时

TaskContinuaationOptions的枚举值列表(MSDN)


            Task<string> task = Task.Factory.StartNew<string>(()=>string.Concat("abc","def"));
            Task comletedTask = task.ContinueWith((antecedentTask) =>
                {
                    //System.Trace.Assert(task.IsCompleted);
                    Console.WriteLine("Completed!!");

                },TaskContinuationOptions.OnlyOnRanToCompletion);

**Task上的未处理任务:如何处理从不同的线程中引发的为处理任务。

异常会在异常层次结构中"冒泡",从而触发“windows错误报告”,而且应用程序会推出,以此来报告错误。

//处理一个任务的未处理异常

            Task task = Task.Factory.StartNew(()=>{throw new ApplicationException();});
            try
            {
                task.Wait();
            }
            catch (AggregateException exception)
            {
              foreach(Exception item in exception.InnerExceptions )
                {
                    Console.WriteLine("ERROR : {0}",item.Message);
                }
            }

//使用ContinueWith()来处理为处理异常

            bool parentTaskFaulted = false;
            Task task = new Task(() => { throw new ApplicationException(); } );
            Task faultedTask = task.ContinueWith(
                (parentTask) =>
                {
                    parentTaskFaulted = parentTask.IsFaulted;
                    //指定只应在延续任务前面的任务引发了未处理异常的情况下才安排延续任务。此选项对多任务延续无效。
                },TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            faultedTask.Wait();
            Trace.Assert(parentTaskFaulted);
            // task.IsFaulted: 如果任务引发了未经处理的异常,则为 true;否则为 false。
            if (!task.IsFaulted)
            {
                task.Wait();
            }
            else
            {
                //task.Exception : 获取导致 System.Threading.Tasks.Task 提前结束的 System.AggregateException。如果 System.Threading.Tasks.Task
                //     成功完成或尚未引发任何异常,这将返回 null。
                Console.WriteLine("ERROR: {0}", task.Exception.Message);
            }

*取消任务:协作式取消 :不是一个线程取消另一个线程,而是API请求一个Task取消。通过检查取消标识(System.Threading.CancellationToken),想要取消的目标任务可恰当的响应取消请求。下面有个类没有实现:piCalculator

    class Program
    {
        static void Main(string[] args)
        {
            //Console.WindowWidth:获取或设置控制台窗口的宽度。
            //.PadRight():返回一个新字符串,该字符串通过在此字符串中的字符右侧填充指定的 Unicode 
            //字符来达到指定的总长度,从而使这些字符左对齐。
            string stars = "*".PadRight(Console.WindowWidth-1,'*');
            Console.WriteLine("Push Enter to exit");

            CancellationTokenSource cancelltionTokenSource = new CancellationTokenSource();
            Task task=Task.Factory.StartNew(()=>WritePi(cancelltionTokenSource.Token),cancelltionTokenSource.Token); 
            Console.ReadLine();
            //传达取消请求
            cancelltionTokenSource.Cancel();
            Console.WriteLine(stars);
            task.Wait();
            Console.WriteLine();
        }

        private static void WritePi(CancellationToken cancellationToken)
        {
            const int batchSize = 1;
            string piSection = string.Empty;
            int i = 0;
            // cancellationToken.IsCancellationRequested:如果已请求取消此标记,则为 true;否则为 false。
            while (!cancellationToken.IsCancellationRequested||i==int.MaxValue)
            {
                piSection = piCalculator.Calculate(batchSize,(i++)*batchSize);
                Console.Write(piSection);
            }
        }
    }

启动Task后,会有ReadLine()阻塞主线程,于此同时,任务继续执行,计算PI的下一位并打印出来,用户按Enter键以后,执行就会遇到一个CancellationTokenSource.Cancel()调用。在task.Cancel()的调用和task.Wait()之间用打印* 号。这样做的目的是证明在观察到取消标记之前,既有可能发生一次额外的迭代。
对Cancel()的调用会从CancellationTokenSource.Token复制的所有取消标志上设置IsCancellationRequseted属性。

  • 取消标识(CancellationToken):会在伊布任务中求值,CancellationToken (struct )看起来和CancellationTokenSource(class)差不多,但是前者用于监视和响应一个取消请求;而后者用于取消任务本身。
  • 复制的(Copied):CancellationToken是一个struct,所以调用CancellationTokenSource.Token会调用标识的一个副本。这样一来,所有的取消标识的实例都是线性安全的。

为了监视IsCancellationRequested属性,CancellationToken的一个实例(从CancellationTokenSource.Token获取)传给并行任务。

关于CancellationToken,另一个注意的要点是重在Register()方法,通过这个方法可以登记一个操作,它会在标识为已取消时调用。换言之,调用register()方法,将向对应的CancellationTokenSource的Cancel()上的一个侦听器委托进行订阅。

**长时间运行的任务

如果开发人员知道一个Task要长时间运行,会长时间霸占一个底层线性资源,开发人员应该告诉线程池共享线程不会太快交还,这样一来,线程池更有可能为任务创建一个专用线程(而不是分配一个共享线程)。在StartNew的时候,使用TaskCreationOptions.LongRunning选项。

Task task=Task.Factory.StartNew(()=>WritePi(cancelltionTokenSource.Token),TaskCreationOptions.LongRunning);

**释放一个任务Dispose().

**并行迭代

看例子就清查啦~~

之前for 循环代码:

            const int TotalDigits = 100;
            const int BatchSize = 10;

            string pi = null;
            int iterations = TotalDigits / BatchSize;
            //PiCalculator.Calculate()的第一个参数是要计算的位数(BatchSize),第二个参数是要计算的起始位(i*BatchSize)
            //for循环同时且顺序的进行每一次迭代
            for (int i = 0; i < iterations; i++)
            {
                pi += PiCalculator.Calculate(BatchSize, i * BatchSize);
            }
            Console.WriteLine(pi);

Paraller.For():只要计算结果顺序追加(append)即可。这个语句除非迭代都完成,否则其调用是不会结束的。

            string pi = null;
            int iterations = TotalDigits / BatchSize;
            string[] sections = new string[iterations];
           // public static ParallelLoopResult For(int fromInclusive,int toExclusive,Action<int> body)
            Parallel.For(0, iterations, (i) =>
                {
                    sections[i] += PiCalculator.Calculate(BatchSize,i*BatchSize);
                });

            pi = string.Join("",sections);
            Console.WriteLine(pi);

将区段的链接放到最后,避免了有尚未计算好的区段或者顺序错误的区段造成的竞态条件。

同时也有一个paraller.Foreach();

        static void EncryptFiles(string directoryPath, string searchPattern)
        {
            IEnumerable<string> file = Directory.GetFiles(directoryPath,searchPattern,
                SearchOption.AllDirectories);
            Parallel.ForEach(file, (fileName) =>
                {
                    EncryptFiles(fileName);
                });
        }


**使用System.AggregateException进行并行异常处理,对于并行操作,该异常可能产生多个异常。

        static void EncryptFiles(string directoryPath, string searchPattern)
        {
            IEnumerable<string> file = Directory.GetFiles(directoryPath,searchPattern,
                SearchOption.AllDirectories);
            try
            {
                Parallel.ForEach(file, (fileName) =>
                {
                    Encrypt(fileName);
                });
            }
            catch (AggregateException excetion)
            {
                Console.WriteLine("ERROR:{0}",excetion.GetType().Name);
                foreach (Exception item in excetion.InnerExceptions)
                {
                    Console.WriteLine("{0}-{1}",item.GetType().Name,item.Message);
                }
            }
       }

*取消并行循环:调用取消请求的那个线程通常不能是正在执行并行循环的那个线程,在下面的代码中,用Task.Factory.StartNew()调用Parallel.ForEach<T>()。采用这种方式,不仅查询以并行方式执行,而且还异步执行,允许代码提示用户“Push Enter to Exit”

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.IO;

namespace Ex17_05
{
    class Program
    {
        static void EncryptFiles(string directoryPath, string searchPattern)
        {
            string starts = "*".PadRight(Console.WindowWidth-1);
            IEnumerable<string> file = Directory.GetFiles(directoryPath, searchPattern,
                SearchOption.AllDirectories);

            CancellationTokenSource cts = new CancellationTokenSource();
            //CancellationTokenSource.Token属性通过并行循环的重载版本与并行循环关联。
            ParallelOptions po = new ParallelOptions { CancellationToken = cts.Token };
            //注册一个将在取消此 System.Threading.CancellationToken 时调用的委托。
            cts.Token.Register(()=>Console.WriteLine("Cancelling...."));
            Console.WriteLine("Push Enter to Exit");
            Task task = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        //   source:可枚举的数据源。
                        //   parallelOptions:一个 System.Threading.Tasks.ParallelOptions 实例,用于配置此操作的行为. 
                        //   body:将为每个迭代调用一次的委托。
                        // 返回结果:一个 System.Threading.Tasks.ParallelLoopResult 结构,其中包含有关已完成的循环部分的信息。    
                        Parallel.ForEach(file, po, (fileName, loopState) =>
                        {
                           Console.WriteLine(fileName);
                        });
                    }
                       //忽略了异常的处理
                    catch (OperationCanceledException )
                    {
                    }
                 });
            Console.ReadKey();
            cts.Cancel();
            Console.Write(starts);
            task.Wait();
 }
     static void Main(string[] args)
        {
            EncryptFiles(@"D:\练习", "*");
            Console.ReadKey();
        }
    }
}


**并行执行LINQ查询(System.Linq.parallelEnumerable):调用并行标准查询运算符的结果是一个并行枚举数,一般是ParallelQuery<T>.这意味着将以并行方式对PLinq结果进行进一步处理。

return data.select((item)=>Encrpt(item)).ToList();=====>return data.Asparallel.select((item)=>Encrpt(item)).ToList();

            //使用标准查询运算符的LINQ
            OrderedParallelQuery<string> parallelGroups;
            parallelGroups = data.AsParallel().OrderBy(item=>item);

            //为表达式使用LINQ
             ParallelQuery<IGrouping<char, string>> parallelGroups1;
             parallelGroups1=  
                from text in data.AsParallel()
                orderby text
                group text by text[0];

  **.net4之前的线程处理

    class Program
    {   public const int Reptitions = 1000;
        static void Main(string[] args)
        {
            //表示在thread上执行的方法
            ThreadStart threadStart = DoWork;
            //初始化Thread类的新实例。
            Thread thread = new Thread(threadStart);
             
            thread.Start();
            for (int i = 0; i < Reptitions; i++)
            {
                Console.Write('-');
            }
            //在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。
            //在.net4.0中,与thread.Wait()等价。
            thread.Join();
            Console.ReadKey();
        }

        public static void DoWork()
        {
            for (int i = 0; i < Reptitions; i++)
            {
                Console.Write('.');
            }
        }
    }

*线程管理

Join()

IsBackGround:当为true 是,允许进程先于线程执行完毕之前终止。

Priority

ThreadState

Thread.Sleep()

Abort()

**线程池处理(System.Threading.ThreadPool)

ThreadPool.QueueUserWorkItem(DoWork,'-');

.....

Thread.Sleep(1000);

.....

public static void DoWork(object state)

{....

}

 

 

 




 




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值