C# 多线程编程

一、简单的例子

using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace multithreading
{
    public partial class Form1 : Form
    {
        private delegate void MyInvoke(string str);//代理
        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            int n = new Random().Next(6) + 1;
            textBox1.Text = n.ToString();
           // Thread thread1 = new Thread(new ThreadStart(CrossThreadFlush));
           // Thread thread2 = new Thread(new ParameterizedThreadStart(ParameterRun));
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground = true;
            thread.Start();
        }
        private void CrossThreadFlush()
        {
            
            while (true)
            {
                //将sleep和无限循环放在等待异步的外面
                Thread.Sleep(1000);
                string time = DateTime.Now.ToString();
                ThreadFuntion(time);
            }
            
        }
        private void ThreadFuntion(string time)
        {
            if (textBox2.InvokeRequired)
            {
                MyInvoke _myInvoke = new MyInvoke(ThreadFuntion); //将代理绑定到方法 
                this.Invoke(_myInvoke, new object[] { time });//调用代理 
            }
            else 
            {
                this.textBox2.Text = time;
            }
        }
        /// <summary>  
        /// 带参数的启动方法  
        /// </summary>  
        /// <param name="ms">让线程在运行过程中的休眠间隔</param>  
        public void ParameterRun(object ms)
        {
            int j = 10;
            int.TryParse(ms.ToString(), out j);//这里采用了TryParse方法,避免不能转换时出现异常  
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(Thread.CurrentThread.Name + "系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
                Thread.Sleep(j);//让线程暂停  
            }
        } 
    }

}


 2、控制多个控件的方法:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace multithreading
{
    public partial class Form1 : Form
    {
        //private delegate void MyInvoke(string str);//代理
        private delegate void MyInvoke();
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int n = new Random().Next(6) + 1;
            textBox1.Text = n.ToString();
           // Thread thread1 = new Thread(new ThreadStart(CrossThreadFlush));
            // Thread thread2 = new Thread(new ParameterizedThreadStart(ParameterRun));
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground = true;
            thread.Start();
        }
        /*
        private void CrossThreadFlush()
        {
            
            while (true)
            {
                //将sleep和无限循环放在等待异步的外面
                Thread.Sleep(1000);
                string time = DateTime.Now.ToString();
                ThreadFuntion(time);
            }
            
        }
        private void ThreadFuntion(string time)
        {
            if (textBox2.InvokeRequired)
            {
                MyInvoke _myInvoke = new MyInvoke(ThreadFuntion); //将代理绑定到方法 
                this.Invoke(_myInvoke, new object[] { time });//调用代理 
                //this.Invoke(_myInvoke);
            }
            else 
            {
                this.textBox2.Text = time;
            }
        }
        /// <summary>  
        /// 带参数的启动方法  
        /// </summary>  
        /// <param name="ms">让线程在运行过程中的休眠间隔</param>  
        public void ParameterRun(object ms)
        {
            int j = 10;
            int.TryParse(ms.ToString(), out j);//这里采用了TryParse方法,避免不能转换时出现异常  
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(Thread.CurrentThread.Name + "系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
                Thread.Sleep(j);//让线程暂停  
            }
        } 
        */

        private void CrossThreadFlush()
        {
            MyInvoke fc = new MyInvoke(ThreadFunction);
            fc.BeginInvoke(null, null);
        }
        private void ThreadFunction()
        {
            while (true)
            {
                this.textBox2.Text =DateTime.Now.ToString();
                this.textBox3.Text = DateTime.Now.ToString();
                Thread.Sleep(1000);
            }

        }
    }
}


二、多线程编程的几种方式

  1  不带参数的启动方式

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

namespace StartThread
{
   class Program
   {
       int interval = 200;
       static void Main(string[] args)
       {
           Program p = new Program();
           Thread nonParameterThread = new Thread(new ThreadStart(p.NonParameterRun));
           nonParameterThread.Start();
       }
       /// <summary>
       /// 不带参数的启动方法
       /// </summary>
       public void NonParameterRun()
       {
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine("系统当前时间毫秒值:"+DateTime.Now.Millisecond.ToString());
               Thread.Sleep(interval);//让线程暂停
           }
   }
}

  2带参数的启动方法
 如果要在实例化线程时要带一些参数,就不能用ThreadStart委托作为构造函数的参数来实例化Thread了,而要ParameterizedThreadStart委托,和ThreadStart一样的是它也是线程启动时要执行的方法,和ThreadStart不同的是,它在实例化时可以用一个带有一个Object参数的方法作为构造函数的参数,而实例化ThreadStart时所用到的方法是没有参数的。
 为什么是Object这样的参数呢?很简单,因为在.net中Object是所有类型的基类,用它可以表示Array(数组)、Interface(接口)、ValueType(值类型,如bool,byte,char,short,int,float,long,double等)、class(类)等.net中的类型。当然,这也意味着如果你要启动一个线程,给它传递一个int类型参数时,必须在启动方法中进行相应的类型转换。

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

namespace StartThread
{
   class Program
   {
       int interval = 200;
       static void Main(string[] args)
       {
           Program p = new Program();

           Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
           parameterThread.Name = "Thread A:";
           parameterThread.Start(30);
       }
  
       /// <summary>
       /// 带参数的启动方法
       /// </summary>
       /// <param name="ms">让线程在运行过程中的休眠间隔</param>
       public void ParameterRun(object ms)
       {
           int j = 10;
           int.TryParse(ms.ToString(), out j);//这里采用了TryParse方法,避免不能转换时出现异常
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine(Thread.CurrentThread.Name+"系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
               Thread.Sleep(j);//让线程暂停
           }
       }
   }
}

在这个方法里,我们在启动线程时顺便指定了线程的暂停间隔,也就是这句:
 parameterThread.Start(30);
 线程启动时运行的方法是public void ParameterRun(object ms),这个值为30的int类型变量被装箱成object,所以在方法中还需要将它转换成int类型,这个可以通过拆箱或者其它办法解决。
 假如我们要启动两个线程,每个线程的暂停间隔不一样,启动代码如下:

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

namespace StartThread
{
   class Program
   {
       int interval = 200;
       static void Main(string[] args)
       {
           Program p = new Program();

           Thread parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
           parameterThread.Name = "Thread A:";
           parameterThread.Start(30);
           //启动第二个线程
           parameterThread = new Thread(new ParameterizedThreadStart(p.ParameterRun));
           parameterThread.Name = "Thread B:";
           parameterThread.Start(60);
       }
  
       /// <summary>
       /// 带参数的启动方法
       /// </summary>
       /// <param name="ms">让线程在运行过程中的休眠间隔</param>
       public void ParameterRun(object ms)
       {
           int j = 10;
           int.TryParse(ms.ToString(), out j);//这里采用了TryParse方法,避免不能转换时出现异常
           for (int i = 0; i < 10; i++)
           {
               Console.WriteLine(Thread.CurrentThread.Name+"系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
               Thread.Sleep(j);//让线程暂停
           }
       }
   }
}


对上面的代码做一点说明,就是线程启动之后,线程的实例不必再存在,例如在上面的代码中我用的是同一个实例实例化了两个线程,并且这两个线程运行很正常。
 
 继续探索
 上面解决了一个问题,如果在启动线程时需要参数如何解决,如果针对上面的问题继续发掘,比如:在启动线程时不但要指定线程的暂停间隔,还需要指定循环次数(在上面的所有例子中都是执行10次的),这个问题该如何解决呢?
 有两种办法可以解决:
 首先可以继续在ParameterizedThreadStart这里做文章,因为这里可以使用一个Object类型的参数,那么可以通过数组或者一个类来解决(因为它们都是Object的子类)。我在做某个系统时确实采用数组处理过这种情况,这样就要求在线程启动方法中必须清楚知道数组中每个参数的用途,不是太方便。
 这里说说重新定义一个实体类来解决的方法,代码如下。

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

namespace StartThread
{
   class MyThreadParameter
   {
       private int interval;
       private int loopCount;
       /// <summary>
       /// 循环次数
       /// </summary>
       public int LoopCount
       {
           get { return loopCount; }
       }

       /// <summary>
       /// 线程的暂停间隔
       /// </summary>
       public int Interval
       {
           get { return interval; }
       }
   /// <summary>
   /// 构造函数
   /// </summary>
       /// <param name="interval">线程的暂停间隔</param>
       /// <param name="loopCount">循环次数</param>
       public MyThreadParameter(int interval,int loopCount)
       {
           this.interval = interval;
           this.loopCount = loopCount;
       }
   }
   class Program
   {
       int interval = 200;
       static void Main(string[] args)
       {
           Program p = new Program();

           Thread parameterThread = new Thread(new ParameterizedThreadStart(p.MyParameterRun));
           parameterThread.Name = "Thread A:";
           MyThreadParameter paramter = new MyThreadParameter(50, 20);
           parameterThread.Start(paramter);
       }
 

       /// <summary>
       /// 带多个参数的启动方法
       /// </summary>
       /// <param name="ms">方法参数</param>
       public void MyParameterRun(object ms)
       {
           MyThreadParameter parameter = ms as MyThreadParameter;//类型转换
           if (parameter != null)
           {
               for (int i = 0; i < parameter.LoopCount; i++)
               {
                   Console.WriteLine(Thread.CurrentThread.Name + "系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
                   Thread.Sleep(parameter.Interval);//让线程暂停
               }
           }
       }
   }
}

第二种方法和上面方法有些相似,也是需要引入外部类,并且将Thread实例放在引入的类中,这种情况适合于在线程中处理的业务逻辑比较复杂的情况。在前不久处理的一个项目中我用过这种情况,它是用来实现双向数据传输的。
 如果实现上面的效果,代码如下:

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

namespace StartThread
{
   class MyThreadParameter
   {
       private int interval;
       private int loopCount;
       private Thread thread;
       
/// <summary>
/// 构造函数
/// </summary>
       /// <param name="interval">线程的暂停间隔</param>
       /// <param name="loopCount">循环次数</param>
       public MyThreadParameter(int interval,int loopCount)
       {
           this.interval = interval;
           this.loopCount = loopCount;
           thread = new Thread(new ThreadStart(Run));
       }

       public void Start()
       {
           if (thread != null)
           {
               thread.Start();
           }
       }

       private void Run()
       {
           for (int i = 0; i < loopCount; i++)
           {
               Console.WriteLine("系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString());
               Thread.Sleep(interval);//让线程暂停
           }
       }
   }
   class Program
   {
       static void Main(string[] args)
       {
           MyThreadParameter parameterThread = new MyThreadParameter(30, 50);
           parameterThread.Start();
       }

   }
}

上面的代码的运行效果和前面的代码运行效果类似,只不过是将业务处理代码放在一个单独的类MyThreadParameter中,使得MyThreadParameter看起来也像一个Thread,实际上维护的还是其内部的Thread,在一些大型系统中这样做的好处是便于维护。
 
 总结:在本篇主要讲述如何启动线程的问题,在启动时可能会遇到无需参数、需要多个参数的情况,在这里讲述了如何解决这些问题的思路。在.net类库中虽然存在着庞大的类库,但是并不是总会有合适的类来解决我们所遇到的问题,但是只要肯动脑筋总会想到合适的办法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值