多线程编程学习笔记-基础(三)

接上文 多线程编程学习笔记-基础(一)

接上文 多线程编程学习笔记-基础(二)

九、向线程传递参数

 1.代码如下。

 

using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入线程
using System.Diagnostics;
 

namespace ThreadConsoleApp
{

    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("开始,给线程传参数");          

            var fore = new ThreadBackground(10);   

            Thread t = new Thread(fore.CountNumber);
            t.Name = "线程1";         

            //启动线程

            t.Start();
            t.Join();
            Console.WriteLine("----------------------------"); 

            var t2 = new Thread(Count);
            t2.Name = "线程2";
            t2.Start(8);
            t2.Join();
            Console.WriteLine("----------------------------"); 

            var t3 = new Thread(()=>CountNumber(12));
            t3.Name = "线程3";
            t3.Start();
            t3.Join();
            Console.WriteLine("----------------------------");

 

            int i = 10;
            var t4 = new Thread(() => PrintNumber(i));
            t4.Name = "线程4";
            i = 20;
            var t5 = new Thread(() => PrintNumber(i));
            t5.Name = "线程5";
            t4.Start();
            t5.Start();
            Console.Read();
        }

        static void CountNumber(int cnt)
        {
            for (int i = 0; i < cnt; i++)
            {

                Thread.Sleep(500);
                Console.WriteLine(string.Format(" {0}    打印 {1,11} 数字", Thread.CurrentThread.Name, i.ToString("N0")));

            }

        }

        static void Count(object cnt)
        {
            CountNumber((int)cnt);
        }
        static void PrintNumber(int num)
        {        

            Console.WriteLine(string.Format(" {0} 打印 {1,11} 数字", Thread.CurrentThread.Name, num.ToString("N0")));         

        } 
    }
}

 

 

 2.结果如下图。

 

线程1,我们通过实例化对象来进行参数传递。

线程2,我们使用Thread.Start()来传递参数,不过此方法只接收单个参数,而且是对象类型。

线程3,我们使用lambda表达式进行参数传递,lambda表达式定义了一个不属于任何类的方法,同时该方法调用了我们实际要执行的方法,同时传递参数给线程。

线程4与线程5,则是显示了使用lambda表达式进行参数传递的一个问题,即当多个lambda表达式共用一个变量时,它们会共享这个变量的值。如图中线程4与线程5所显示,没有打印10,只打印了20。

 

十、使用lock锁定操作

 1.代码如下

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入线程
using System.Diagnostics;
 

namespace ThreadConsoleApp
{
    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("开始,给线程加锁");           

            var c = new Counter();           

            Thread t = new Thread(()=>Count(c));
            var t3 = new Thread(() => Count(c));

             var t2 = new Thread(() => Count(c));
            t.Name = "线程1";         

            //启动线程
            t.Start();
         

            t2.Name = "线程2";
            t2.Start();     
 
            t3.Name = "线程3";
            t3.Start();
            t.Join();
            t2.Join();     
            t3.Join();
            Console.WriteLine(string.Format("没有加锁的多线程总计:{0}",c.Count));
            Console.WriteLine("----------------------------");


            var c1 = new CounterLock();
            var t4 = new Thread(() => Count(c1));
            t4.Name = "线程4";
           

            var t5 = new Thread(() => Count(c1));
            t5.Name = "线程5";
            var t6 = new Thread(() => Count(c1));
            t6.Name = "线程6";

            t4.Start();
            t5.Start();
            t6.Start();
            t4.Join();
            t5.Join();
            t6.Join();
            Console.WriteLine(string.Format("加锁的多线程总计:{0}", c1.Count));
            Console.Read();
        }      

        static void Count(CountBase cnt)
        {
            for (int i = 0; i < 100000; i++)
            {
                cnt.Incerement();
                cnt.Dncerement();

            }
        }
     }

    abstract class CountBase
    {
         public abstract void  Incerement();
        public abstract void Dncerement();
    }

    class Counter : CountBase
    {
        public int Count { get; private set; }
        public override void Dncerement()
        {
            Count--;
        }

        public override void Incerement()
        {

            Count++;
        }
    }

    class CounterLock : CountBase
    {
        private readonly object objSync = new object();
        public int Count { get; private set; }
        public override void Dncerement()
        {
            lock (objSync)
            {
                Count--;
            }       
        }

        public override void Incerement()
        {
            lock (objSync)
            {
                Count++;
            }
        }
    }

}

 

2. 结果如下图

 

      主线程首先创建了一个 Counter的实例对象,这个类定义了一个可以增,可以 减的简单计数器。然后我们创建了三个线程,这三个线程共享一个Counter对象。由于没有对共享变量的锁定,所以在一个周期内,对共享变量的改变,在上个线程没结束之前,当前线程又对共享变量进行了操作,我们会得到不同的计数值,如上图所示。为了防止这种情况的发生,所以我们要对共享变量进行加锁。使用lock关键字锁定对象,这样在一个线程操作完成之前,其他线程都不能对共享变量进行操作。

 

十一、Moniter对资源的锁定

 1.代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入线程
using System.Diagnostics; 

namespace ThreadConsoleApp
{   
 class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("开始,线程死锁");          

            var lock1 = new object();
            var lock2 = new object();
           

            Thread t = new Thread(()=> DeadLock(lock1,lock2));
            t.Name = "线程1";       

            //启动线程

            t.Start();
            lock (lock2)
            {
                Thread.Sleep(2000); 

                if (Monitor.TryEnter(lock1,TimeSpan.FromSeconds(5)))

                {
                    Console.WriteLine("在规定时间内,请求资源");

                }
                else

                {
                    Console.WriteLine("超时,无法获取资源");

                }
            }

            new Thread(() => DeadLock(lock1, lock2)).Start();
             Console.WriteLine("-----------------------------");

            lock (lock2)
            {

                Thread.Sleep(1000);
                Console.WriteLine(string.Format("死锁线程"));

               lock(lock1)
                {
                    Console.WriteLine("请求资源成功");

                }          
           }

            Console.Read();
        }
      

        /// <summary>
        /// 死锁方法
        /// </summary>
        /// <param name="objLock1"></param>
        /// <param name="objLock2"></param>
        static void DeadLock(object objLock1,object objLock2)
        {
            lock (objLock1)
            {
                Thread.Sleep(2000);
                lock (objLock2)
                {
                    Console.WriteLine("死锁");
                }
            }
        }
    }
}

2.结果如下图

 

 先看deadlock方法,这个方法先锁定lock1对象,然后等待2秒之后,锁定了lock2对象。然后在子线程中启动了这个方法。

主线程中先锁定了lock2对象,然后等待获取lock1对象。由于子线程锁定了lock1对象,等待lock2对象。所以造成了死锁。

 

十二、多线程的异常处理

 1.代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; //引入线程
using System.Diagnostics;
 
namespace ThreadConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("开始,异常处理");          
            Thread t = new Thread(FaultyThread);
            t.Name = "线程1";        

            //启动线程
            t.Start();
            t.Join();
            try
            {
                t = new Thread(ExpectThread);
                t.Start();
            }

            catch (Exception ex)
            {
                Console.WriteLine("异常信息:" + ex.Message);
            }
            Console.Read();
        }    

        static void ExpectThread()
        {
            Console.WriteLine(string.Format("异常处理"));
            Thread.Sleep(2000);
            throw new Exception("抛出异常");
        }
        static void FaultyThread()
        {
            try
            {
                Console.WriteLine(string.Format("异常处理2"));
                Thread.Sleep(1000);
                throw new Exception("抛出异常2");
            }

            catch (Exception ex)
            {
                Console.WriteLine(string.Format("异常处理2:{0}",ex.Message));
            }        

        }
    }
}

 

 2.结果如下图。

 

 在程序中定义了两个处理异常的方法,一个对异常进行了处理,另一个没有对异常进行处理。最后如图。程序崩溃了。

posted @ 2017-10-25 14:15 DotNet菜园 阅读(...) 评论(...) 编辑 收藏