65.9K
CodeProject 正在变化。 阅读更多。
Home

多线程之旅

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.46/5 (6投票s)

2010年5月17日

CPOL

3分钟阅读

viewsIcon

31164

downloadIcon

1701

面向初学者的多线程教程和示例。

Screenshot.PNG

引言

众所周知,免费午餐已经结束。下一代应用程序旨在利用处理器的强大功能。 现在的挑战是构建软件,使其能够尽可能快地响应用户的活动,从而提供丰富的用户体验,同时进行必要的计算,以便尽快向用户呈现数据。 这是多线程有用的一个例子。 在其他情况下,多线程也很有用,例如

  1. 执行需要很长时间的操作。
  2. 当任务具有不同的优先级时。将线程设置为高优先级来执行时间关键任务,低优先级线程执行其他任务。
  3. 当您想要持续监视某些资源时。

因此,开发人员编写利用多线程的应用程序有很多好处。 众所周知,线程是一个广阔的主题,无法在一篇文章中涵盖。 我正在考虑撰写一系列文章来描述与多线程相关的各种主题。 截至目前,这些主题尚未确定。 所以放松一下,我们即将开始构建多线程应用程序之旅。

创建一个新线程

可以使用以下方法创建一个线程

Thread thread = new Thread(FunctionName);

首先,让我们创建一个控制台应用程序。 这是一个自我记录的代码片段

class Program
    {
        static void Main(string[] args)
        {
            //Main Thread of the application is Named as "Main Thread"
            Thread.CurrentThread.Name = "Main Thread";
            Console.WriteLine("From Main() function\n");
            //printing Main thread name and ID.
            //One can get the ID of the thread by enquiring 
            //"Thread.CurrentThread.ManagedThreadId"
            Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n",
		Thread.CurrentThread.Name,Thread.CurrentThread.ManagedThreadId);
            
            //Create an Object of the class Compute, 
            //through which we can access Instance Method.
            Compute compute = new Compute();            
            Thread Th1 = new Thread(Compute.StaticMethod); //new thread Th1 
					//is created by passing static method.
            Th1.Name = "Th1"; //Let's Name the thread as Th1.
            Th1.Start(); // Let's start the Thread.

            Thread Th2 = new Thread(compute.InstanceMethod);
            Th2.Name = "Th2";
            Th2.Start();

            Console.ReadLine();
        }
    }

    class Compute
    {
        public Compute()
        {
            Console.WriteLine("Initialized the Compute class");
            Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n", 
		Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
        }

        public static void StaticMethod()
        {
            Console.WriteLine("From the Static Method in the Compute class");
            Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n", 
		Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.Name);
            Thread.Sleep(200); // thread which is now running 
			// Static method is made to sleep for 200 micro Seconds.
        }

        public void InstanceMethod()
        {
            Console.WriteLine("From the Instance Method in the Compute class");
            Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n", 
		Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
            Thread.Sleep(200);
        }

        public void DataMethod(object Message)
        {
            Console.WriteLine("From the Instance Data Method in the Compute class");
            string s = Message as string;

            if (s != null)
            {
                Console.WriteLine("Data Received :{0}", s);
                Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n", 
		Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
            }
        }
    }

线程构造函数已重载,线程类具有采用 ThreadStart 委托或 ParameterizedThreadStart 委托的构造函数;该委托包装了您调用 Start 方法时由新线程调用的方法。 这是一个代码片段,仅显示使用 ThreadStart 委托创建线程。 Compute 类保持不变。

class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main Thread";
            Console.WriteLine("From Main() function\n");

            //Create a ThreadStart Delegate object and pass the function.
            ThreadStart tStartStatic = new ThreadStart(Compute.StaticMethod);
            //Create the thread
            Thread Th1 = new Thread(tStartStatic);
            Th1.Name = "Th1"; 	//Set Name.
            Th1.Start(); 		//Start the thread.

            Compute compute = new Compute();
            ThreadStart tStartInstance = new ThreadStart(compute.InstanceMethod);
            Thread Th2 = new Thread(tStartInstance);

            Th2.Start();
            Th2.Name = "Th2"; 	// Note: Named the thread after Thread start. 
				// will Name be set to Th2 ?
            Console.ReadLine();
        }
    }

这是使用“ParameterizedThreadStart”创建线程的代码片段。

class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main Thread";
            Console.WriteLine("From Main() function\n");

            Compute compute = new Compute();
            //Delegate object is created and is pointed to a function. 
            ParameterizedThreadStart pThreadStart = 
		new ParameterizedThreadStart(compute.DataMethod);

            // A new thread is created with the delegate.
            Thread thread = new Thread(pThreadStart);
            thread.Name = "Parameterized Thread"; // set the Name for the thread.
            thread.Start("Hello World"); //Pass the arguments  to thread.start function.
            Console.ReadLine();
        }
    }

让我们有一个回调委托,当线程完成其工作时触发。 这是一个代码片段

namespace ConAppThreadingEx2
{
    class Program
    {
        static void Main()
        {
            //Name the thread.
            Thread.CurrentThread.Name = "Main Thread";
            Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n", 
		Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);

            //Create an object of CallBackDelegate type and point to 
            //ResultFromThread function.
            CallBackDelegate cb = new CallBackDelegate(ResultFromThread);
            //Create an object of type Compute, look at delegate object 
            //is used in constructor.
            Compute compute = new Compute(50, cb);
            //Create a thread. No difference, just a one more variation.
            Thread t = new Thread(new ThreadStart(compute.ThreadPro));
            t.Name = "ThreadPro"; //set the name for the thread.
            t.Start(); // Start the thread.
            Console.Read();
        }

        //this function is called when thread completes its work.
        static void ResultFromThread(int i)
        {
            Console.WriteLine
	    ("Thread just returned from the ThreadPro method performing:{0} Tasks", i);
        }
    }

    public delegate void  CallBackDelegate(int i);

    class Compute
    {
        int numberOfTasks;
        CallBackDelegate CallBackDlg;

        public Compute(int Tasks, CallBackDelegate cb)
        {
            numberOfTasks = Tasks;
            CallBackDlg = cb;
        }

        //Thread is pointed to this function. Delegate is called 
        //at the end of the function.
        //Which triggers the ResultFromThread function.
        public void ThreadPro()
        {
            Console.WriteLine("From ThreadPro Method in Compute Class");
            Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n",
		Thread.CurrentThread.Name,Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000); // simulating numberOfTasks
            //check for CallBackDlg and trigger.
            if (CallBackDlg != null)
                CallBackDlg(numberOfTasks);
        }
    }
}

让我们把所有这些学习应用到一个更大的例子中——我们使用一个 GUI 应用程序来演示线程的创建、启动、暂停、恢复和停止。 在这里,创建一个新线程来绘制图像。 这是一个将彩色图像转换为灰度图像的示例; 这里转换有意放慢速度,因为主要目的是演示线程——并且用户可以直观地看到线程的暂停、恢复和停止。 应用程序有趣的部分是线程挂起和线程恢复,以及使用 this.Invoke() 在线程和 UI 线程之间的通信。

按钮、文本框、标签等控件由 UI 线程创建。 创建一个新线程并且在新线程上工作流程期间,新线程将无法控制 UI 上的这些控件对象,因为这些控件是由 UI 线程创建的。 对于工作线程可以在控件上调用 Invoke() 函数的情况,有一种解决方案,该函数反过来通过委托触发该函数。 应用程序的下一个有趣的部分是线程的挂起、恢复和中止。 当线程正在运行时(检查 ThreadState 的状态),中止线程非常简单。 但是,当线程挂起或请求挂起是线程状态时,在这种情况下,线程中止可能会导致崩溃。

CLR 不会中止挂起的线程,但它会将线程置于垃圾收集器可以回收内存的状态。 因此,最好的解决方案是恢复挂起的线程并中止该线程。

看看 ThreadingA 源代码。

历史

  • 版本 1.0.0.0
© . All rights reserved.