一個多線程應用程序中的線程彼此間可能關聯也可能不關聯。例如,在每個程序中都有一個用來生成其他子線程的主線程,所以主線程就成了所有其他線程的控制器。在一個多線程應用程序中有三種常用方法來定義線程間的關系:
1. 主線程和工作線程模型
2. 對等線程模型
3. 管道線程模型
我們將詳細討論每一個模型,借助一些代碼來使你能夠知道如何在自己的程序中實現它們。
主線程和工作線程模型
這是最常見的線程模型也是到目前為止本書一直使用的模型。如圖3表示:
圖 3
在主線程和工作線程模型中,主線程接收所有的輸入並把輸入參數傳遞給其他線程以便於執行一些特定的任務。主線程可以等待/不等待工作線程完成。在這個模型中,工作線程不直接與輸入資源打交道,而是通過主線程作為中轉。例如,我們有一個Windows 窗體程序,上面有三個按鈕分別觸發三個不同事件:
1. 從一個web service 獲得數據
2. 從一個數據庫獲得數據
3. 做一些諸如截取XML文件的任務
這是最簡單的線程模型。主線程包含在Main() 方法中,這個模型在客戶端界面程序中非常常見。
讓我們看一些代碼以更好地描述這個問題。現在看一下一個窗體程序:
當你單擊一個按鈕,它將觸發一個執行一些運算的工作線程並在按鈕下面的空白處顯示返回結果。我們不對界面做詳細討論;這里只給出部分代碼,全部代碼請從這里下載。
/************************************* /* Copyright (c) 2012 Daniel Dong * * Author:oDaniel Dong * Blog:o www.cnblogs.com/danielWise * Email:o guofoo@163.com * */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace MainWorker { public class MainWorker { public ArrayList CalculatorFactors(double number) { if (number < 3) { return null; } else { ArrayList factors = new ArrayList(); factors.Add("1"); for (double current = 2; current <= number - 1; current++) { if ((double)(Math.Floor(number / current) * current) == number) { factors.Add(current.ToString()); } } factors.Add(number.ToString()); return factors; } } public long CalculatorFactorial(int number) { if(number < 0) { return -1; } if (number == 0) { return 1; } else { long returnValue = 1; for (int current = 1; current <= number; current++) { returnValue *= current; } return returnValue; } } } }
上面的代碼非常易於理解而且為了模塊化的原因被包裝到一個類中。第一個方法返回一個包含所有傳遞給它的數的階乘的ArrayList, 而第二個方法簡單地返回一個長整型數字。記住階乘的變化是非常快的。13的階乘是6,227,020,800. 計算階乘的方法沒有占用處理器很長時間,但是它可以用來描述這個模型。
public partial class frmCalculator : Form { MainWorker threadMthods; delegate void UpdateValue(string text); public frmCalculator() { InitializeComponent(); threadMthods = new MainWorker(); }
構造函數實例化了一個MainWorker對象。下面我們展示按鈕事件處理方法的代碼:
private void cmdFactors_Click(object sender, EventArgs e) { Thread calculatorFactors = new Thread(new ThreadStart(FactorsThread)); calculatorFactors.Start(); } void FactorsThread() { ArrayList val = threadMthods.CalculatorFactors(200); StringBuilder sb = new StringBuilder(); for (int count = 0; count <= val.Count - 1; count++) { sb.Append((string)val[count]); if (count < val.Count - 1) { sb.Append(","); } } //Create and invoke the delegate with the new value UpdateValue updVal = new UpdateValue(DisplayValue); string[] args = { sb.ToString() }; this.Invoke(updVal, args); }
cmdFactors_Click() 方法使用FactorsThread()方法實例化一個新的線程,這個線程會對MainWorker.CalculatorFactors() 方法返回值進行格式化處理。
這個方法需要包裝起來因為線程方法不可以有返回值。
private void cmdFactorials_Click(object sender, EventArgs e) { Thread calculatorFactorial = new Thread(new ThreadStart(FactorialThread)); calculatorFactorial.Start(); } void FactorialThread() { long val = threadMthods.CalculatorFactorial(20); //Create and invoke the delegate with the new value UpdateValue updVal = new UpdateValue(DisplayValue); string[] args = { val.ToString() }; this.Invoke(updVal, args); }
FactorialThread() 方法就更加簡單一些。不論何時cmdFactorial 按鈕被點擊,主線程都會觸發一個新線程並會等待結果准備好以后更新lblResult 文本框。
這是一個關於主線程和工作線程的非常簡單的例子。顯然這個例子可以很容易的改變成處理數據庫的連接的方法,或者其他更耗時的活動。
然而,當你使用這個模型時你應該小心處理幾個線程相關的問題。你可能使用線程生成線程,並讓線程訪問同一資源,最終導致線程進入無限循環狀態。
這是最簡單的模型,但是也是需要程序員做最多工作的模型。
還有,在這個模型中各個線程間相互獨立,每個線程都由其父線程控制 - 這個例子中就是主線程。
下一篇將介紹對等線程模型...


