C#中多线程的用法

1、在C#中使用多线程可以使用Thread
代码例子:

 public class ThreadExample
 {
     public static void ThreadProc()
     {
         for (int i = 0; i < 10; i++)
         {
             Console.WriteLine("ThreadProc: {0}", i);
         }
     }
     public static void Start()
     {          
         Thread t = new Thread(new ThreadStart(ThreadProc));
         t.Start();
         for (int i = 0; i < 4; i++)
         {
             Console.WriteLine("主线程开始工作.");
         }
         Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.");
         t.Join();        
         Console.WriteLine("工作结束");
         Console.ReadLine();
     }
 }
 
 internal class Program
 {
     static void Main(string[] args)
     {
	 ThreadExample.Start();
     }
}

由于我们写了 t.Join(); 所以,工作结束一定是最后一步进行的,但是。在Start方法中主线程和副线程是交替打印的,那么如何控制线程的进行呢?
有多种方法
在这之前,我先写一个例子,你们猜猜打印出来的结果是多少?

 public class Sample1
 {
     public static void Show()
     {
         for (int i = 0; i < 5; i++)
         {
             new Thread(AddOne).Start();
         }
         Thread.Sleep(TimeSpan.FromSeconds(5));    
         Console.WriteLine("sum = " + sum);
         Console.ReadKey();
     }
     private static int sum = 0;
     public static void AddOne()
     {
         for (int i = 0; i < 100_0000; i++)
         {
             sum += 1;
         }
     }
 }

可以看出来我将1000000累加了5次,但是结果不是5000000,原因就是在累加过程中,sum是交替执行的
所以,在这种情况下,我们必须使用同步锁,

public class Sample2
{
    public static void Show()
    {
        object obj= new object(); //使用锁对象进行线程同步
        for (int i = 0; i < 5; i++)
        {
            new Thread(()=>AddOne(obj)).Start();
        }
        Thread.Sleep(TimeSpan.FromSeconds(5));
        lock (obj) 
        {
            Console.WriteLine("sum = " + sum);
        }
        Console.ReadKey();
    }
    private static volatile int sum = 0;
    public static void AddOne(object obj)
    {
        for (int i = 0; i < 100_0000; i++)
        {
            lock (obj) 
            {
                sum += 1;
            }
        }
    }
}

这样结果就是5000000了,但是正常开发中,我们一般用Task,很少用到Thread了,(代表我自己)
我们继续修改代码

 public class TaskSample1
 {
     public static void Show()
     {
         var task=Task.Run(() => 
         {
             for (int i = 0; i < 100; i++)
             {
                 Console.WriteLine("ThreadProc: {0}", i);
             }
         });
         task.Wait();
         for (int i = 0; i < 100; i++)
         {
             Console.WriteLine("主线程: 执行方法。");
         }
         Console.WriteLine("主线程:ThreadProc。加入已恢复。按“Enter”结束程序。");
         Console.ReadLine();
     }
 }

但是如果我们的Task很多呢 需要 Task.WaitAll(tasks);

 public class TaskSample2
 {
     public static void Show()
     {
        
         Task[] tasks = new Task[10];
         for (int i = 0; i < 10; i++)
         {
             tasks[i] = Task.Run(() => Thread.Sleep(2000));
         }
         try
         {
             Task.WaitAll(tasks);
         }
         catch (AggregateException ae)
         {
             Console.WriteLine("One or more exceptions occurred: ");
             foreach (var ex in ae.Flatten().InnerExceptions)
                 Console.WriteLine("   {0}", ex.Message);
         }

         Console.WriteLine("Status of completed tasks:");
         foreach (var t in tasks)
             Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
         Console.WriteLine("主线程:ThreadProc。加入已恢复。按“Enter”结束程序。");
         Console.ReadLine();
     }
 }

现在我们已经学会基础的线程控制了,
但是一个任务的进行,会遇到很多的问题,不是你简单的写两个任务,这两个任务就会按照你的想法去执行下去
比如,
1、在执行下载任务的时候没电了,
2、在执行下载任务的时候想取消了
这个时候,你该怎么做呢
看代码:

public class Sample4
{
    public static void Show()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        // 可以直接设置10秒后任务停止 CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
        cts.Token.Register(() => { Console.WriteLine("任务已停止"); });
        Task.Run(() =>
        {
            int i = 0;
            while (!cts.IsCancellationRequested)
            {
                i++;
                Console.WriteLine(i);
                Task.Delay(1000).Wait();
            }
        });
        // cts.CancelAfter(5000);        //5秒后取消操作 也可以使用Cannel直接取消
        //这个可以用在你下载文件,或者读取文件出错的时候使用

        //也可以设置按下ESC键时任务停止
        var key = Console.ReadKey();
        if (key.Key == ConsoleKey.Escape)
        {
            cts.Cancel();
        }
        Console.ReadLine();
    }
}

这段代码中,你每隔一秒累加1,功能就是按下ESC键,停止累加操作,结束程序
CancellationTokenSource你把它当作一个回调函数,
可以在任务进行的时候取消任务,
在C#中About()方法已经过期了,现在大多数都用CancellationTokenSource来代替。

我在上面写过, object obj= new object(); //使用锁对象进行线程同步
我们要同步线程的时候用到。
但是在大型项目中,我们还是用AutoResetEvent锁和ManualResetEvent锁,我在下期再讲

posted @ 2023-08-14 16:36  孤沉  阅读(89)  评论(0编辑  收藏  举报