线程和进程学习

进程和线程

线程没有父子关系,任务有父子关系

1.每一个进程的内存空间是可以共享的,每一个线程都可以使用这些内存空间
2.互斥锁(Mutual exclusion),缩写Mutex,允许一个线程读写某一块内存区域,
防止多个线程同时读写某一块内存区域
3.信号量,允许固定数目的线程读取某一块内存区域,保证多个线程不会互相冲突,
防止固定数目的线程之外的其他线程同时读取某一块内存区域
4.2可以用3代替,但2效率高

线程开启方式一 -委托

代码:

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

namespace 线程和进程
{
    class Program
    {
        //一般我们会对比较耗时的操作开启单独的线程去执行,比如下载操作
        static int Text(int a)
        {
            Console.WriteLine("这是一个有返回值,有参数的线程Text   " + a);
            Thread.Sleep(100);//让当前线程休眠(暂停线程的执行)
            return 90;
        }
        static void Text1(int a)
        {
            Console.WriteLine("这是一个无返回值,有参数的线程Text1   " + a);
        }
        static void Text2()
        {
            Console.WriteLine("这是一个无返回值,无参数的线程Text2   ");
        }
        static void Main(string[] args)//在main线程中执行,一个线程里面的语句是从上到下执行的
        {
            //通过一个委托启动一个无返回值,有参数的线程
            Action<int> a2 = Text1;
            a2.BeginInvoke(20, null, null);//输出值

        //通过一个委托启动一个无返回值,无参数的线程
        Action a3 = Text2;
        a3.BeginInvoke(null, null);//输出值

        //通过一个委托开启一个有返回值,有参数的线程
        Func<int,int> a = Text;
        //IAsyncResult 取得当前线程的状态
        IAsyncResult ia = a.BeginInvoke(100,null, null);//开启一个新的线程去执行a所引用的方法,//输出值,不输出返回值

        //可以认为线程是同步执行的(异步执行)
        Console.WriteLine("main");
        //1.第一种办法检测线程结束
        while (ia.IsCompleted == false)//当前线程没有执行完毕
        {
            Console.Write(".");
            Thread.Sleep(10);//控制检测的频率
        }
        int res1 = a.EndInvoke(ia);//取得异步线程的返回值
        Console.WriteLine("通过IsCompleted取得返回值" + res1);

        //2.第二种办法检测线程结束
        bool isEnd = ia.AsyncWaitHandle.WaitOne(1000);//1000毫秒表示超时时间,如果等待了1000毫秒线程好没有结束,这个
                                                      //方法返回false,在1000毫秒内结束了,返回true
        if (isEnd)
        {
            int res2 = a.EndInvoke(ia);
            Console.WriteLine("取得异步线程的返回值" + res2);//取得异步线程的返回值
        }

        //3.第三种办法检测线程结束,通过回调
        //倒数第二个参数是一个委托类型的参数,表示回调函数,线程结束的时候会调用这个委托指向的方法,倒数第一个参数
        //用来给回调函数传递参数
        IAsyncResult ia1 = a.BeginInvoke(100, OnCallBack, a);//开启一个新的线程去执行a所引用的方法  //输出值,不输出返回值

        //4.通过lambda表达式使用回调函数检测线程结束
        a.BeginInvoke(100, ar=> 
        {
            int res4 = a.EndInvoke(ar);
            Console.WriteLine("通过lambda表达式回调函数取得返回值" + res4);
        }, null);//开启一个新的线程去执行a所引用的方法  //输出值,不输出返回值

        Console.ReadKey();
    }
    static void OnCallBack(IAsyncResult ia1)
    {
        Func<int, int> a = ia1.AsyncState as Func<int, int>;
        int res3 = a.EndInvoke(ia1);
        Console.WriteLine("通过回调函数取得返回值" + res3);//取得异步线程的返回值
    }
}
}

线程开启方式二 -Thread

通过Thread调用的线程,如果有参数,参数得是Object类型的
Program.cs

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

namespace 线程开启方式2
{
    class Program
    {
        static void Text1()
        {
            Console.WriteLine("开始下载");
            Thread.Sleep(3000);
            Console.WriteLine("下载完成");
        }
        static void Text2(object o)
        {
            Console.WriteLine("开始下载:{0}",o);
            Thread.Sleep(3000);
            Console.WriteLine("下载完成");
        }
        static void Main(string[] args)
        {
            Thread thread0 = Thread.CurrentThread;//获取当前线程
            Console.WriteLine("输出主线程的信息:");
            Console.WriteLine("ID:{0}", thread0.ManagedThreadId);//线程ID
            Console.WriteLine("Name:{0}", thread0.Name);//当前线程的名字
            Console.WriteLine("IsBackground:{0}", thread0.IsBackground);//前台线程还是后台线程
            Console.WriteLine("Priority:{0}", thread0.Priority);//线程优先级

        //开启无返回值,无参数的线程
        Thread thread = new Thread(Text1);//创建thread对象,这个线程还没有启动
        thread.Start();                   //执行线程

        //开启无返回值,有参数的线程
        Thread thread2 = new Thread(Text2);//创建thread对象,这个线程还没有启动
        thread2.Start("xxx.种子");         //执行线程

        //
        Download d = new Download("yy.zip", "www.baidi.com");
        Thread thread3 = new Thread(d.Show);
        thread3.Start();

        Console.ReadKey();

    }
}
}

Download.cs

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

namespace 线程开启方式2
{
    class Download
    {
        public string fileName;
        public string filePath;
        public Download() { }
        public Download(string name,string path)
        {
            this.fileName = name;
            this.filePath = path;
        }
        public void Show()
        {
            Console.WriteLine("下载开始:{0},下载的路径:{1}",fileName,filePath);
            Thread.Sleep(3000);
            Console.WriteLine("下载完成");

        }
    }
}

线程的其他概念(后台和前台,优先级,线程的状态)

后台线程和前台进程:
默认情况下,Thread创建的线程都是前台线程。线性池总是后台线程。
前台线程运行完毕后,如果后台线程还在运行,所有的后台线程都降被终止。

控制线程

Thread thread = new Thread(Text2);//这个线程是前台线程
//thread.IsBackground = true;//设置为后台线程
thread.Start("hh.zip");
thread.Abort();//终止线程
//t.Join();让当前thread线程睡眠,等待t线程执行完,然后继续运行thread线程以及下面的代码
Console.ReadKey();

线程开启方式三 -线程池

线性池总是后台线程。
不能把入池的线程改为前台线程
不能设置入池的线程的优先级和名称
入池的线程只能用于时间比较短的任务,如果一直运行(如拼写检查器),就应该用Thread类创建一个线程。

代码:

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

namespace 线程开启方式2
{
    class Program
    {
        static void ThreadMethod(object o)
        {
            Console.WriteLine("线程开始"+Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(3000);
            Console.WriteLine("线程结束");
        }
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(ThreadMethod);//调用的线程得有一个参数,开启一个工作线程,(ThreadMethod,参数)。
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            Console.ReadKey();

        }
    }
}

运行截图

线程开启方式四 -任务

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

namespace 线程开启方式2
{
    class Program
    {
        static void ThreadMethod()
        {
            Console.WriteLine("任务开始");
            Thread.Sleep(3000);
            Console.WriteLine("任务结束");
        }
        static void Main(string[] args)
        {
            //任务开启的第一种方式
            //Task t = new Task(ThreadMethod);//传递一个需要线程去执行的方法
            //t.Start();

            //任务开启的第二种方式
            TaskFactory tf = new TaskFactory();
            Task t = tf.StartNew(ThreadMethod);

            Console.WriteLine("main");
            Console.ReadKey();

        }
    }
}

连续任务、任务层次结构

我们在一个任务中启动一个新的任务,相当于新的任务是当前任务的子任务,两个任务异步执行,如果父任务执行完了但是子任务没有执行完,它的状态会设置为WaitingForChildrenToComplete,只有子任务也执行完了,父任务的状态就变成RunToCompletion

连续任务代码:

static void DoFirst(){
Console.WriteLine("do  in task : "+Task.CurrentId);
Thread.Sleep(3000);
}
static void DoSecond(Task t){
	Console.WriteLine("task "+t.Id+" finished.");
	Console.WriteLine("this task id is "+Task.CurrentId);
	Thread.Sleep(3000);
}
Task t1 = new Task(DoFirst);
Task t2 = t1.ContinueWith(DoSecond);//t2在t1任务完成之后执行
Task t3 = t1.ContinueWith(DoSecond);//t3在t1任务完成之后执行
Task t4 = t2.ContinueWith(DoSecond);//t4在t2任务完成之后执行

任务层次结构代码:

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

namespace 线程开启方式2
{
    class Program
    {
        static void ParentTask()
        {
            Console.WriteLine("task id " + Task.CurrentId);
            var child = new Task(ChildTask);
            child.Start();
            Thread.Sleep(1000);
            Console.WriteLine("parent started child , parent end");
        }
        static void ChildTask()
        {
            Console.WriteLine("child");
            Thread.Sleep(5000);
            Console.WriteLine("child finished ");
        }
        static void Main(string[] args)
        {
            var parent = new Task(ParentTask);
            parent.Start();
            Thread.Sleep(2000);
            Console.WriteLine(parent.Status);
            Thread.Sleep(4000);
            Console.WriteLine(parent.Status);

            Console.ReadKey();

        }
    }
}

线程问题:争用和死锁

解决死锁的办法:在编程的开始设计阶段,设计锁定顺序

MyThreadObject.cs

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

namespace 线程开启方式2
{
    class MyThreadObject
    {
        private int state = 5;
        public void ChangeState()
        {
            state++;
            if (state ==5)
            {
                Console.WriteLine("state=5");
            }
            state = 5;
        }
    }

}

Program.cs

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

namespace 线程开启方式2
{
    class Program
    {
        static void ChangeState(object o)
        {
            MyThreadObject m = o as MyThreadObject;
            while (true)
            {
                lock (m)//向系统申请锁定m对象,如果m对象没有被锁定,那么可以,如果
                        //m对象被锁定了,那么这个语句会暂定,直到申请到m对象,锁定一个引用类型,不能锁定值类型
                {
                    m.ChangeState();//同一时刻,只有一个线程在使用这个方法
                }//释放对m的锁定
                
            }
           
        }
        static void Main(string[] args)
        {
            MyThreadObject m = new MyThreadObject();
            Thread t = new Thread(ChangeState);
            t.Start(m);
            new Thread(ChangeState).Start(m);
            Console.ReadKey();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值