目录

C多线程

C#多线程

启动方式

Task:基于Thread+ThreadPool结合

Task三种启动方式比较

【1】Task对象的Start方法启动,适合在符合条件情况下去启动,也就是可以按照需求启动,灵活控制。

【2】Run方法,是定义后直接启动。适合马上启动的情况。

【3】Task.Factory.StartNew,也是直接启动线程。(了解)

        //【1】创建Task对象,然后调用Start方法启动线程
        static void Test1()
        {
            Task task = new Task(() =>
             {
                 Console.WriteLine("new 一个新的Task,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
             });
            task.Start();
        }
        //【2】使用Task的静态方法Run方法直接启动线程
        static void Test2()
        {
            Task task = Task.Run(() =>
            {
                Console.WriteLine("使用Task中的Run方法,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
            });
        }
        //【3】使用Factory启动
        static void Test3()
        {
            Task task = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("使用Task中的Factory.StartNew方法,对应的子线程ID=" + Thread.CurrentThread.ManagedThreadId);
            });
        }

线程启动取消暂停继续

https://i-blog.csdnimg.cn/direct/3b23ad665d0e47c0925f5c948a98e364.png

取消再启动会重新从1开始

cts、mRestEvent是被多个线程共享的

Register是回调方法(执行完自动执行某个方法),每个线程结束都会执行一次

//取消任务信号源对象,是被多个线程共享的
private CancellationTokenSource cts = new CancellationTokenSource();
//手动停止事件对象,放在线程执行过程中,true,不会被阻塞
private ManualResetEvent mRestEvent = new ManualResetEvent(true);
private void button1_Click(object sender, EventArgs e)
{
    if (cts.IsCancellationRequested)
        cts = new CancellationTokenSource();
    Task task = Task.Run(() =>
    {
        int cnt = 0;
        while (!cts.IsCancellationRequested)
        {
            //用来控制是否需要暂停和继续
            mRestEvent.WaitOne();
            cnt++;
            Thread.Sleep(300);
            Console.WriteLine(cnt);
        }
    },cts.Token);
    cts.CancelAfter(3000);//3s后自动取消
    //只要线程取消后就会执行
    cts.Token.Register(() => { Console.WriteLine("任务取消,开始清理工作"); });
}

private void button2_Click(object sender, EventArgs e)
{
    cts.Cancel();
}

private void button3_Click(object sender, EventArgs e)
{
    mRestEvent.Reset();//将事件状态设置为未发送信号(false)被阻塞
}

private void button4_Click(object sender, EventArgs e)
{
    mRestEvent.Set();//将事件状态设置为已发送信号(true)
}

线程等待

//等待所有线程完成
Task.WaitAll(task1, task2);
//只需等待一个线程完成
Task.WaitAny(task1, task2);
Console.WriteLine("主线程开始运行");

线程延续

Task.WhenAll(task1, task2).ContinueWith() //都执行完就执行第三个线程

Task.WhenAny(task1, task2).ContinueWith() //只要有一个执行完就执行第三个线程

        private void button1_Click(object sender, EventArgs e)
        {
            if (cts.IsCancellationRequested)
                cts = new CancellationTokenSource();
            Task task1 = Task.Run(() =>
            {
                Console.WriteLine("task1");
            }, cts.Token);
            Task task2 = Task.Run(() =>
            {
                Console.WriteLine("task2");
            }, cts.Token);
            Task.WhenAll(task1, task2).ContinueWith(x =>
            {
                Console.WriteLine("task3");
            });
            Console.WriteLine("主线程开始运行");
        }

跨线程访问卡顿

https://i-blog.csdnimg.cn/direct/08d281ab0e3b47f686b6d4150d558d8c.png

主线程创建的控件,是不能直接被子线程访问的,要使用

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

将耗时任务和其他任务分开就不会卡顿了,可以点击其他按钮

public Main()
{
    InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
    Task task = new Task(() =>
    {
         //Thread.Sleep(5000);模拟耗时任务,会卡,不能做其他事
        this.label1.Text = "abcd";
    });
    task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}

private void button3_Click(object sender, EventArgs e)
{
    Task task = Task.Run(() =>
    {
        Thread.Sleep(5000);
        
    });
    //延续任务,耗时任务后执行,可以做其他事
    task.ContinueWith(t => 
    {
        this.label1.Text = "abcd123";
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

LongRunning运行长时间的任务

Task.Run()要用Func带返回值委托

        private void button1_Click(object sender, EventArgs e)
        {
            
            Task task1 = new Task(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("111");
            },TaskCreationOptions.LongRunning);
            task1.Start();
        }

对某一个资源(对象)加锁,防止线程争夺,其他线程必须等待

private int nums = 0;
private object myLock = new object();//可以不用定义,除非是静态方法

public void TestCount()
{
    //lock (myLock)
    //{
    //    for (int i = 0; i < 100; i++)
    //    {
    //        nums++;
    //        Console.WriteLine(nums);
    //    }
    //}
    //
    lock (this)  //只能锁住线程能访问到的对象
    {
        for (int i = 0; i < 100; i++)
        {
            nums++;
            Console.WriteLine(nums);
        }
    }
}       
public void Start()
{
    for (int i = 0; i < 5; i++)
    {
        Task.Run(() =>
        {
            TestCount();
        });
    }
}

aysnc/await

配合使用

    class Program
    {
        static void Main(string[] args)
        {
            string str = GetText().Result;
            Console.WriteLine(str);
        }
        async static Task<string> GetText()
        {
            FileStream fs = new FileStream("mytxt.txt", FileMode.Open);
            byte[] filebytes = new byte[fs.Length];
            await fs.ReadAsync(filebytes, 0, filebytes.Length);
            return Encoding.UTF8.GetString(filebytes);
        }
    }