標籤: 教學

Android APP 非同步下載圖片實作(背景服務)-APP開發教學

  此篇文章主要是示範教學如何開發Android APP時,利用背景服務來進行比較耗時的工作,使得使用者可以不會覺得APP停頓當掉的錯覺。

   背景服務程式(背景執行緒)主要在比較複雜的APP應用程式,它在執行運作時,例如從網路下載檔案、讀取後端資料庫的資料、讀取手機記憶卡的資料時,會需要較多時間,所以系統畫面沒辦法即時回應,甚至出現【應用程式沒有回應】的對話視窗,詢問使用者是否繼續等待或結束APP程式。

  

    而透過在背後另外建立自己的執行緒,去獨立執行比較耗時的工作任務,跟主畫面的執行緒沒有關聯,完全不會影響主畫面的更新與停頓狀態.

     這類性質的工作就適合利用 Android API 提供的 AyncTask 執行緒.

下面我在 res/laylout 設計一個簡單的APP主畫

繪圖

<?xml version="1.0″ encoding="utf-8″?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#607D8B">

<!– 顯示圖片的元件 –>

<ImageView

android:id="@+id/imageview"

android:layout_width="match_parent"

android:layout_height="match_parent" />

<!– 開始下載圖片的按鈕元件 –>

<Button

android:id="@+id/download_btn"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:layout_margin="6sp"

android:onClick="clickDownLoadButton"

android:text="背後執行下載圖片"

android:textSize="24sp" />

<LinearLayout

android:id="@+id/op_panel"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignBottom="@id/imageview"

android:layout_centerInParent="true"

android:background="@drawable/operation_drawable"

android:orientation="horizontal"

android:padding="6sp"

android:visibility="invisible">

<!– 顯示前一張圖片 –>

<ImageButton

android:id="@+id/previous"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1″

android:onClick="clickMoveButton"

android:src="@drawable/ic_keyboard_arrow_left_white_48dp" />

<!– 顯示下一張圖片 –>

<ImageButton

android:id="@+id/next"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1″

android:onClick="clickMoveButton"

android:src="@drawable/ic_keyboard_arrow_right_white_48dp" />

</LinearLayout>

</RelativeLayout>

程式解說:

上面主要畫面配置檔,提供一個按鈕,可以下載7張圖片,並將下載的圖片依序可以輪流顯示每張圖片,每張圖片都是透過背後執行緒進行下載,檔執行任務完成後,在將 LinearLayout容器顯示出來,這個容器很簡單放入兩個 ImageButton ,用來切換上一

張與下一張圖片。

現在回到 Java 主程式,宣告一個繼承

AppCompatActivity 的類別

public class AsyncTaskDemo extends AppCompatActivity {

private ImageView imageview;

private LinearLayout op_panel;

private Bitmap[] images;

// 目前畫面顯示的圖片編號

private int position = 0;

// 顯示下載進度用的進度對話框

private ProgressDialog progressDialog;

程式解說:


在上面主要宣告幾個欄位 Fields , 用來對應之前的配置畫面元件,還有 Bitmap[] 陣列,存放下載的圖片,以及一個進度用的是從 PrgressDialog。

然後重點在 Button 圖片下載的程式碼邏輯:

public void clickDownLoadButton(View view) {

// 這邊可以讓下載按鈕消失

findViewById(R.id.download_btn).setVisibility(View.INVISIBLE);

// 建立下載圖片的AsyncTask物件

final DownloadImageTask downloadImageTask = new DownloadImageTask();

// 顯示 進度對話框

progressDialog = new ProgressDialog(this);

progressDialog.setTitle(“Download");

progressDialog.setMessage(“Please wait for download…");

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.setProgress(0);

progressDialog.setCancelable(false);

// 加入取消工作的按鈕

progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, “Cancel",

new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

// 取消AsyncTask的工作,參數指定為true,表示取消正在執行的工作

// 呼叫這個方法的AsyncTask物件,在結束doInBackground方法後,

// 不會呼叫onPostExecute方法,改為呼叫onCancelled方法

downloadImageTask.cancel(true);

}

});

// 顯示進度對話框

progressDialog.show();

// 啟動AsyncTask物件

downloadImageTask.execute();

}

上面程式主要宣告一個 DownloadImageTask ,

這個程式主要是繼承了 AsyncTask 類別

// 呼叫這個物件的execute方法後就會執行這個方法

// 參數是AsyncTask泛型指定的第一個型態的陣列

@Override

protected Void doInBackground(Void… args) {

// 讀取陣列資源,下載的圖片名稱

Resources res = getResources();

String[] imageNames =

res.getStringArray(R.array.android_icons_array);

// 讀取儲存圖片的網路位置

String baseUrl = getString(R.string.base_url);

// 設定進度對話框的最大進度數量

progressDialog.setMax(imageNames.length);

for (int i = 0; i < imageNames.length; i++) {

// 判斷是否取消工作

if (isCancelled()) {

break;

}

// 下載圖片的完整網址

String url = baseUrl + imageNames[i];

// 從網際網路下載圖片

downloads.add(loadBitmap(url));

// 執行進度處理,參數會傳送給onProgressUpdate方法

publishProgress(i + 1);

}

// 如果已經下載圖片

if (downloads.size() > 0) {

// 建立儲存圖片的Bitmap陣列

images = new Bitmap[downloads.size()];

// 把List物件轉換為陣列

downloads.toArray(images);

}

return null;

}

// 執行進度處理,參數是AsyncTask泛型指定的第二個型態的陣列

// 在doInBackground方法中,呼叫publishProgress方法後,

// 就會執行這個方法並接收放在publishProgress方法中的參數

@Override

protected void onProgressUpdate(Integer… values) {

// 設定進度對話框的進度

progressDialog.setProgress(values[0]);

// 設定ImageView使用的ImageView物件

imageview.setImageBitmap(downloads.get(downloads.size() – 1));

}

// doInBackground方法結束後就會執行這個方法

// 參數是AsyncTask泛型指定的第三個型態的陣列

// 接收doInBackground方法的回傳值

@Override

protected void onPostExecute(Void result) {

// 結束進度對話框

progressDialog.dismiss();

// 設定ImageView使用的ImageView物件

imageview.setImageBitmap(images[0]);

// 顯示上下張圖片的操作按鈕

op_panel.setVisibility(View.VISIBLE);

}

// 呼叫cancel方法後執行這個方法

// 參數是AsyncTask泛型指定的第三個型態的陣列

// 接收doInBackground方法的回傳值

@Override

protected void onCancelled(Void result) {

if (images != null) {

// 設定ImageView使用的ImageView物件

imageview.setImageBitmap(images[0]);

// 顯示上下張圖片的操作按鈕

op_panel.setVisibility(View.VISIBLE);

}

}

完成上面所有程式,就完成一個非同步的下載圖片實作案例,這邊我是預期看這篇實作教學的本身對 Android 開發有一定的經驗,如果對於這篇教學有任何問題,可以寄發郵件向我詢問。

程式執行結果

繪圖2

繪圖3

網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰

業務合作、軟體委外開發

業務窗口:allen@netqna.com

聯繫電話:0920-883-870

公司電話:02-55991310

公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓

skype: netqna

line:netqna

微信:netqna

黃先生 Allen

my_qrcode_1519621481105

感謝日明耀診所委託開發 iPad APP 醫療教學系統(APP開發、軟體開發)

       在與台北知名的日明耀中醫診所互相討論多次關於一系列的影片互動開發、測驗考題系統,今天正式進行簽署合約進行合作,也再次讓我們軟體開發團隊可以深入中醫醫療的領域知識(Know-How),我們也將獨立開發遊戲引擎來設計互通教學過程,來加強使用者學習的良好體驗。

timg

網智數位-軟體開發,專門針對客戶進行量身設計的客製化軟體,已有多年經驗,也在不同產業領域都有實作成功案例。

網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen

my_qrcode_1519621481105

Xamarin 跨平台程式開發-訊息傳遞 – 軟體開發 教學

   在 Xamarin 開發 IOS、Android、UWP APP時,常常會遇到訊息接受與訊息傳遞,那麼這邊我想簡單示範如何在 Xamarin 實作一個訂閱訊息程式開發技巧。

Android 執行結果畫面

螢幕快照 2018-07-25 下午10.27.43螢幕快照 2018-07-25 下午10.28.49螢幕快照 2018-07-25 下午10.29.10

   Xamarin 透過 MessagingCenter 這個靜態類別來支援訂閱/通知訊息,這個類別主要需要知道三個 Methods ,分別為 Subscribe<TSender, TArgs> 與 Send<TSender, TArgs>、Unsubscribe<TSender, TArgs>。

Subscribe<TSender, TArgs> 方法用來訂閱訊息,當有訊息接受到時,可以通知使用者,例如出現對話訊息、畫面顯示訊息。

Send<TSender, TArgs> 方法用來通知訊息,已告知那些訂閱訊息者。

Unsubscribe<TSender, TArgs> 原先進行訂閱訊息接受,現在進行取消訂閱,後續如果有訊息通知,都不進行接受訊息。

程式範例示範

image

public class MainPageViewModel
     {
         public ObservableCollection<string> Greetings { get; set; }


        public MainPageViewModel ()
         {
             Greetings = new ObservableCollection<string> ();


           MessagingCenter.Subscribe<MainPage> (this, “哈嘍", (sender) => {
                 Greetings.Add(“哈嘍");
             });


            MessagingCenter.Subscribe<MainPage, string> (this, “哈嘍", (sender, arg) => {
                 Greetings.Add(“哈嘍 " + arg);
             });

         }
     }

在上面程式範例 宣告 一個 MainPageViewModel Class,這個作為 ViewModel 用途,在建構式裡實作訂閱服務,這個機制可以減少程式物件耦合度。

下面這行程式碼,主要是用於實作一個名叫 “哈嘍”的訂閱服務  MessagingCenter.Subscribe<MainPage> (this, “哈嘍", (sender) => {  Greetings.Add(“哈嘍"); });


在底下的主程式 MainPage ,實作訂閱通知服務,主要程式邏輯,我透過註解程式說明

public partial class MainPage : ContentPage
     {
         public MainPage()
         {
             InitializeComponent();


            //這邊我綁定一個ViewModel
             BindingContext = new MainPageViewModel();


            //宣告一個按鈕物件,當點選按鈕時,觸發一個發送訊息
             var button1 = new Button { Text = “Say 哈嘍" };
             button1.Clicked += (sender, e) =>
             {
                 MessagingCenter.Send<MainPage>(this, “哈嘍");
             };


            //宣告一個按鈕物件,當點選按鈕時,觸發一個發送訊息,並傳遞內容參數,傳遞的內容是 Allen
             var button2 = new Button { Text = “Say Hi to Allen" };
             button2.Clicked += (sender, e) =>
             {
                 MessagingCenter.Send<MainPage, string>(this, “哈嘍", “Allen");
             };


            //宣告一個按鈕物件,當點選按鈕時,觸發一個取消訂閱服務,並取消對話視窗訊息!
             var button3 = new Button { Text = “取消訂閱服務,並取消對話視窗訊息!" };
             button3.Clicked += (sender, e) =>
             {
                 MessagingCenter.Unsubscribe<MainPage, string>(this, “哈嘍");
                 DisplayAlert(“取消訂閱服務",
                     “This page has stopped listening, so no more alerts; however the ViewModel is still receiving messages.",
                     “OK");
             };


            //訂閱服務(背後的ViewModel還是仍然保持本身實作的訂閱服務),並出現對話視窗訊息
             MessagingCenter.Subscribe<MainPage, string>(this, “哈嘍", (sender, arg) =>
             {
                 DisplayAlert(“訊息已接受", “參數訊息 為 " + arg, “OK");
             });


            //綁定接受到的訂閱訊息
             var listView = new ListView();
             listView.SetBinding(ListView.ItemsSourceProperty, “Greetings");


            //放置物件內容
             Content = new StackLayout
             {
                 Padding = new Thickness(0, 20, 0, 0),
                 Children = { button1, button2, button3, listView }
             };
         }


在 iPhone X 執行畫面如下

螢幕快照 2018-07-25 下午10.20.29螢幕快照 2018-07-25 下午10.20.46

網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen
my_qrcode_1519621481105

【軟體開發、系統開發】多執行緒多任務存取相同變數,隔離保留各自任務的值 ThreadLocal 技術講解

      在講解幾篇關於多工、多執行緒、平行作業的技術文章後,想來談談另外一個深入的關鍵多多執行緒的問題,那就是多執行緒(多工)作業模式下,如何存取相同變數值,而且又希望各自的執行緒對相同變數又有各自的“值”。

      在 .Net 技術平台下,有個 ThreadLocal<T> 的泛型類別,這個類別可以專門用於有多個執行緒需要共同存取一個變數,但每個執行緒對這個變數的值,又個別記錄隔離儲存,互不影響。我舉個例子來說明,有執行緒A、執行緒B、執行緒C、執行緒D,這四個執行緒平行作業,將共同存取一個變數 X ,但在執行緒A存取變數X時,設定值為 10;執行緒B存取變數X時,設定值為 20;執行緒C存取變數X時,設定值為 30;執行緒D存取變數X時,設定值為 40; 在執行緒A設定 X變數值為 10時,並不會影響到 執行緒 B/C/D 各自設定的 X 變數值。

     一樣在此,我透過一個程式範例來進行實例說明,這個專案類型就簡單用 Console 控制台類型來引導…

完整程式碼如下:

namespace NetQna.Demo
{
     /// <summary>
     /// ThreadLocal 示範教學(網智數位-軟體開發)
     /// </summary>
     public class Program
     {
         /// <summary>
         /// 所有執行緒共同存取的變數值
         /// </summary>
         static ThreadLocal<int> X = new ThreadLocal<int>();


        static void ExecThreadTask(object obj)
         {
             ThreadEntity currThread = (ThreadEntity)obj;
             //在各自的執行緒環境設定 X 變數值,觀察各自的執行緒下,X 變數值各自記錄
             X.Value = currThread.ThreadValue;


            //顯示出 目前所在的執行緒 的值
             Console.WriteLine($"現在的執行緒 ID:{Thread.CurrentThread.ManagedThreadId},{nameof(X)}變數值為:{X.Value}。");
             //顯示出 目前所在的執行緒 的值
             Console.WriteLine($"現在的執行緒 名稱:{currThread.ThreadName},{nameof(X)}變數值為:{X.Value}。");
             Console.WriteLine();
         }


        static void Main(string[] args)
         {


            Dictionary<string, int> threadABCD = new Dictionary<string, int> { { “A", 10 }, { “B", 20 }, { “C", 30 }, { “D", 40 } };


            //分別啟動 A、B、C、D 執行緒,並執行 存取 X 變數值
             foreach (var t in threadABCD)
             {
                 ThreadEntity obj = new ThreadEntity { ThreadName = t.Key, ThreadValue = t.Value };


                //Action<string, int> action = ExecThreadTask(t.Key, t.Value);
                 Thread currThread = new Thread(ExecThreadTask);
                 currThread.Start(obj);


                //這邊我刻意停頓 1 秒,以利顯示各自執行緒
                 Thread.Sleep(1000);
             }


            Console.Read();


            X.Dispose(); //X 變數 釋放
         }


         public class ThreadEntity
         {
             public string ThreadName { get; set; }
             public int ThreadValue { get; set; }


        }
     }
}

程式說明講解

1

一開始我宣告一個屬性變數為 X ,也就是今天講解主要的類別 ThreadLocal,
待會就是用 這個 X 變數,來讓所有執行緒進行存取設定 X 變數值。


2

宣告一個帶有 object 參數的方法,待會就是要讓所有執行緒去各自執行這個方法,這個方法主要我將  object 型態 的 參數 obj 轉型為我自己宣告的類別 ThreadEntity 類別(PS:因為 Thread 建構子對應的方法的參數只能是 object 型態,所以我透過轉型來避開這限制)。

public class ThreadEntity
{
     public string ThreadName { get; set; }
     public int ThreadValue { get; set; }


}

最關鍵的地方,我在這個方法設定了各自執行緒要對應的 X 變數值

//在各自的執行緒環境設定 X 變數值,觀察各自的執行緒下,X 變數值各自記錄
X.Value = currThread.ThreadValue;

3

我這邊分別建立 A、B、C、D 四個執行緒,然後分別設定他們待會要指定 X 變數的值為(10、20、30、40)。

程式執行結果如下圖
d

可以看出 A、B、C、D執行緒各自維護自己 X 變數的值。


參考文章
C#多工作業與平行處理技術講解
1.透過 Thread 類別撰寫多執行緒多工作業。
2.透過 Delegate 委託支援的方法,撰寫非同步任務。
3-1.透過 Parallel 類別操控多任務平行作業(一)。
3-2. 透過 Parallel 類別操控多任務平行作業(二)。
4.使用 Task 自行控制非同步任務作業。
5.在非同步作業時,如何取消非同步任務。
6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。
7.非同步存取變數的問題。
8.非同步資源鎖定解決方式。



網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen

在非同步作業時,如何取消非同步任務(軟體開發、程式設計、軟件開發)

  在之前我寫過一篇關於用 Task 類別來撰寫非同步化作業(使用 Task 自行控制非同步任務作業),來利用多CPU、多核心處理器的優勢,這樣可以避免某些邏輯需要較長的時間去執行,而造成主界面程式停頓卡卡,讓使用者誤以為當機、或程式有問題,所以透過.Net 的 Task 任務,可以讓系統自行的調度限制可以用的執行緒(Thread)。

   既然有了 Task 任務類別的支援,可以讓我們輕易的撰寫多工、非同步作業的程式,讓長時間執行的程式透過另外一個執行緒(Thread)在背後執行,例如我常幫工廠或企業寫物料需求計算(MRP、MPS計算)、業績獎金計算、APS先進排程規劃,這些都是需要執行讀取大量的系統資料來進行運算,這就可以充分利用 ThradTask的技術再背後另外一個執行緒去執行,使用者,在前端主畫面還可以繼續執行其他作業,而不用只能等待程式執行完畢。

    不過我們也常常遇到常時間執行的程式,我們可以提供一個按鈕讓使用者去選擇【取消】背後執行的任務,那麼這篇技術文章我就是想講解,如何在.Net 平台取消非同步化作業的任務 ,.net 平台裡有個類別專門跟 Task 、Task<TResult> 類別搭配使用,使得完成取消非同步任務作業的執行,這個類別就是 CancellationTokenSource 類別


我在這邊一樣透過 Step By Step 來建立一個範例程式,來說明整個程式的撰寫技巧…


1.這邊我簡單為了好示範,我建立一個 Windows Form 視窗專案,命名為 CancellationTokenSource_Demo,如下圖:

繪圖


2.在 Form1 表單,我設計了一下畫面

繪圖2

在Form1表單,我為了示範,所以放置了2個 Label (分別顯示 輸入一個正整數、運算結果顯示),2個 Button 按鈕(分別為 進行運算、取消非同步任務),還有一個 ProgressBar (作用於非同步任務執行時,可以顯示進度條狀態)。


3.在 Form1.cs 的後端程式,是最重要的,我把程式標註註解,這樣大家就可以容易理解了。


public partial class Form1 : Form
{
     public Form1()
     {
         InitializeComponent();
     }


    /// <summary>
     /// 宣告一個 用於支援取消非同步任務的變數
     /// </summary>
     private CancellationTokenSource cts = null;


    private void btnCalc_Click(object sender, EventArgs e)
     {
         //取得的正整數
         int currNum = 0;
         if (!int.TryParse(txtInputInt.Text, out currNum))
         {
             MessageBox.Show(“必須輸入一個正整數數字!");
             return;
         }


        //清楚執行結果
         txtFinalResult.Clear();


        btnStartCalc.Enabled = false; //將【進行運算】按鈕啟用狀態取消
         btnCancel.Enabled = true; //將【取消非同步任務】按鈕啟用狀態啟用


        //用於顯示報告進度條的變數
         IProgress<int> progRpt = new Progress<int>((p) =>
         {
             this.progressBar1.Value = p;
         });


        //如果 CancellationTokenSource 物件變數不是為 null , 比較進行釋放(務必)
         if (cts != null)
         {
             cts.Dispose();
         }


        //實體化 CancellationTokenSource 物件
         cts = new CancellationTokenSource();


        //宣告一個任務變數
         Task<string> task = new Task<string>(() =>
         {
             BigInteger bint = new BigInteger(1d);
             double totalProgress = (double)currNum;


            //如果CancellationTokenSource 物件的 IsCancellationRequested 值為 true ,代表使用者已經將非同步任務進行取消
             for (int i = 1; i <= currNum && !cts.IsCancellationRequested; i++)
             {
                 bint *= i; //相乘
                 double progressVal = Convert.ToDouble(i) / totalProgress * 100d;


                //顯示非同步任務執行的進度表
                 progRpt.Report(Convert.ToInt32(progressVal));
             }


            //傳回執行結果
             return bint.ToString();


        }, cts.Token, TaskCreationOptions.LongRunning);


        //開始執行非同步任務
         task.Start();


        //等待任務操作完成,等待1秒
         while (!task.Wait(1000))
         {
             Application.DoEvents();
         }


        //顯示運算結果
         txtFinalResult.Text = task.Result;
         btnStartCalc.Enabled = true;
         btnCancel.Enabled = false;
     }


    private void btnCancel_Click(object sender, EventArgs e)
     {
         //使用者執行取消非同步任務
         if (cts != null)
         {
             cts.Cancel();
         }
     }
}


1

2

3


4


4.經過步驟3核心程式碼後,就可以編譯執行,執行結果畫面如下:


4-1.

執行運算中…
r1

4-2.

中途,我執行【取消非同步任務】按鈕

r2


完成上述這樣的示範,就可以撰寫讓使用者中途取消任務的執行。



參考文章
C#多工作業與平行處理技術講解
1.透過 Thread 類別撰寫多執行緒多工作業。
2.透過 Delegate 委託支援的方法,撰寫非同步任務。
3-1.透過 Parallel 類別操控多任務平行作業(一)。
3-2. 透過 Parallel 類別操控多任務平行作業(二)。
4.使用 Task 自行控制非同步任務作業。
5.在非同步作業時,如何取消非同步任務。
6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。
7.非同步存取變數的問題。
8.非同步資源鎖定解決方式。




網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen

使用 Task 自行控制非同步任務作業【軟體開發、軟件設計、客製化軟體、軟體設計】

   在之前我寫過兩篇關於使用 Parallel 類別進行多任務的平行作業 ,1.透過 Parallel 類別操控多任務平行作業(一)。 2.透過 Parallel 類別操控多任務平行作業(二)。 , 但因為使用 Parallel 類別雖然方便,因為都被.Net 包裝著很好,各自的執行緒任務獨自運作,如果有時我們想要更進一步的控制每個平行任務任務時,就必須使用  Task<TResult> 與 Task 類,這兩個類別可以有效地自行控制建立非同步任務,可以由程式開發人員決定什麼時候要啟用任務、什麼時候要中斷任務。


(補充)Task<TResult> 類別是從 Task 類別衍生而來,兩者主要差異是,前者是帶一個 泛型參數,也表示任務執行完畢要返回的類型值。


     這邊為了簡單講解 Task 類別的用法,為了好示範講解,一樣透過新增一個 Windows Form 專案,在對應的控制箱事件,撰寫核心程式碼….不過在看實際範例程式碼教學時,我先介紹如何三種使用 Task 任務的技巧:

(1)直接使用 Task.Run()的靜態方法,該方法一旦被呼叫,就會馬上進行非同步化任務,而呼叫也會返回一個 Task 實體物件。


(2)新增一個 Task 物件時,傳遞一個委派方法至建構函數,當建立一個 Task 實體物件時,不會馬上執行非同步任務,而是要手動呼叫 Start() 方法,才會開始執行非同步任務。


(3)Task 與 Task<Result> 類別都有公開一個 Factory 靜態屬性,該屬性返回一個 TaskFactory 或 TaskFactory<Result>類別,我們必須再透過 TaskFactory 對應的方法來啟用非同步任務作業。


(實作過程)


步驟 1:

透過 Visual Studio  2017 來建立一個 Windows Form 專案。

2017-07-12_00-33-55


步驟 2:

在 Form1 表單,我設計版面與拖拉相關控制項至主畫面,有 一個 Label (用於顯示標題,Text =“輸入一個正整數值:” ; 兩個 TextBox (分別用於讓使用者輸入 一個正整數、與顯示非同步任務執行完畢時,要顯示的結果值);還有一個 Button 控制項(Text =“開始計算任務” , Name =”btnExecTask”) ; 最後還有一個 ProgressBar , 目的是要 Task 執行非同步作業時,可以及時更新進度,通知目前作業進度表給使用者。


主畫面截圖如下:

11


步驟 3 :

在 Button 按鈕的 click 事件撰寫主要的核心程式碼:

private void btnExecTask_Click(object sender, EventArgs e)
  {
      uint calcNum = 0;


     if (!uint.TryParse(this.txtNumValue.Text, out calcNum))
      {
          MessageBox.Show(“請輸入一個正整數數字!!");
          return;
      }


     //進行更新進度表
      IProgress<int> progress = new Progress<int>((p) => progressBar1.Value = p);


     //宣告一個 Task 類型變數
      Task<long> task = new Task<long>(() =>
      {
          long result = 0L;
          for (int i = 1; i <= calcNum; i++)
          {
              //累加值
              result += i;


             //進度值
              double currProgress = Convert.ToDouble(i) / Convert.ToDouble(calcNum) * 100d;


             //向主執行緒 主畫面 進行更新進度條
              progress.Report(Convert.ToInt32(currProgress));
          }


         return result;
      });


     this.txtResult.Text = “目前正在執行計算過程中……";


     //任務 手動啟動
      task.Start();


     //等待任務執行完畢過程中,將按鈕先停用,以防止重複執行
      this.btnExecTask.Enabled = false;


     while (!task.Wait(100))
      {
          //在等待過程,允許程式繼續處理其他佇列訊息
          Application.DoEvents();
      }


     //任務執行完畢,重新啟用按鈕
      this.btnExecTask.Enabled = true;


     //顯示計算結果
      txtResult.Text = “任務執行完畢,計算結果值為:" + task.Result.ToString();

  }

c


PS.主要程式碼解釋,我都寫在程式碼註解,寫得還滿詳細的….


步驟 4:

執行 compile ,進行測試執行吧…

result





參考文章

C#多工作業與平行處理技術講解

1.透過 Thread 類別撰寫多執行緒多工作業。

2.透過 Delegate 委託支援的方法,撰寫非同步任務。

3-1.透過 Parallel 類別操控多任務平行作業(一)。

3-2. 透過 Parallel 類別操控多任務平行作業(二)。

4.使用 Task 自行控制非同步任務作業。
5.在非同步作業時,如何取消非同步任務。

6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。

7.非同步存取變數的問題。

8.非同步資源鎖定解決方式。







網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰

業務合作、軟體委外開發

業務窗口:allen@netqna.com

聯繫電話:0920-883-870

公司電話:02-55991310

公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓

skype: netqna

line:netqna

微信:netqna

黃先生 Allen

在.Net 透過 Parallel 類別操控多任務平行作業(二)【軟體開發、軟體設計、系統開發、客製化軟體】

    這篇主要是再次講解 Parallel 類別並行作業的其他支援方法,主要是延續上一篇教學文章 3-1.透過 Parallel 類別操控多任務平行作業(一)。 

    這邊我將透過WPF專案實作一個使用 Parallel.ForEach 方法來支援的循環平行作業任務,它的概念就好比我們常在寫的 for(i=0;i<=10;i++)、或 foreach 迴圈,只是Parallel 類別是充分利用多個處理器、多個核心同時進行作業,執行速度當然效率就快很多。



步驟1:

在 Visual Studio 一樣建立一個WPF 專案

wpf

步驟2:

在 MainWindows.xmal 界面設計成如下結果

parellel

主要是使用了 3個 textbox 控制項,用於讓使用者輸入 資料夾路徑 、想建立的檔案數量、以及每個檔案的大小,再來使用一個 button 按鈕(開始執行 Parallel.Foreach 多任務運算), 來撰寫主要的 並行作業邏輯程式碼。


xaml 程式碼部分畫面
xaml2


步驟3:

在 Button.Click 事件撰寫主要程式碼


程式碼如下:

private void OnClick(object sender, RoutedEventArgs e)
{
     if (string.IsNullOrWhiteSpace(txtDir.Text))
     {
         MessageBox.Show(“請輸入目標存放文件的資料夾!!");
         return;
     }


    //如果目標資料夾不存在,就建立一個新的資料夾
     if (!Directory.Exists(txtDir.Text))
     {
         Directory.CreateDirectory(txtDir.Text);
     }


    int fileNum = 0;
     if (!int.TryParse(txtFileNum.Text, out fileNum))
     {
         MessageBox.Show(“請輸入檔案的數量!!"); return;
     }


    long fileSize = 0L;
     if (long.TryParse(txtSize.Text, out fileSize) == false)
     {
         MessageBox.Show(“請輸入預計每個檔案的大小!!");
         return;
     }


    //記錄要產生新的檔案名稱清單
     List<string> fileNames = new List<string>();


    for (int n = 0; n < fileNum; n++)
     {
         //檔案名稱
         string _filename = “file_" + (n + 1).ToString();


        //資料夾的絕對路徑
         string _dirpath = System.IO.Path.GetFullPath(txtDir.Text);


        //新的檔案的完整路徑
         string fullPath = System.IO.Path.Combine(_dirpath, _filename);


        //加入清單
         fileNames.Add(fullPath);
     }


    txtDisplay.Clear();


    //宣告一個Random 亂書產生器,目的待會用來產生每個檔案的隨機內容
     Random rand = new Random();


    //宣告一個 Action 委派任務,來產生每個檔案與檔案內容
     Action<string> act = (file) =>
         {
             FileInfo fileInfo = new FileInfo(file);


            //如果文件已經存在,就把它刪除
             if (fileInfo.Exists)
                 fileInfo.Delete();


            //開始進行隨機寫入內容
             byte[] buffer = new byte[fileSize];
             rand.NextBytes(buffer);


            using (FileStream fs = fileInfo.Create())
             {
                 BinaryWriter sw = new BinaryWriter(fs);
                 sw.Write(buffer);
                 sw.Close();
                 sw.Dispose();
             }


            //顯示執行結果
             string msg = string.Format(“檔案{0}已經建立完成!!\n", fileInfo.Name);
             this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() => txtDisplay.AppendText(msg)));


        };


    //開始進行 Parallel 的循環平行任務作業
     Parallel.ForEach<string>(fileNames, act);


}



重點程式碼講解:

1.使用 Random rand = new Random();  ,來隨機產生每個檔案的內容。

2.宣告一個 Action 委派任務,來產生每個檔案與檔案內容。

Action<string> act = (file) =>
     {
         FileInfo fileInfo = new FileInfo(file);


        //如果文件已經存在,就把它刪除
         if (fileInfo.Exists)
             fileInfo.Delete();


        //開始進行隨機寫入內容
         byte[] buffer = new byte[fileSize];
         rand.NextBytes(buffer);


        using (FileStream fs = fileInfo.Create())
         {
             BinaryWriter sw = new BinaryWriter(fs);
             sw.Write(buffer);
             sw.Close();
             sw.Dispose();
         }


        //顯示執行結果
         string msg = string.Format(“檔案{0}已經建立完成!!\n", fileInfo.Name);
         this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() => txtDisplay.AppendText(msg)));


    };

3.使用 Parallel.ForEach<string> 來開始進行 Parallel 的循環平行任務作業。

參考文章

C#多工作業與平行處理技術講解

1.透過 Thread 類別撰寫多執行緒多工作業。

2.透過 Delegate 委託支援的方法,撰寫非同步任務。

3-1.透過 Parallel 類別操控多任務平行作業(一)。

3-2. 透過 Parallel 類別操控多任務平行作業(二)。
4.使用 Task 自行控制非同步任務作業。
5.在非同步作業時,如何取消非同步任務。
6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。
7.非同步存取變數的問題。
8.非同步資源鎖定解決方式。




網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen

在.Net 透過 Parallel 類別操控多任務平行作業(一)【軟體開發、軟體設計、系統開發】

   此篇文章主要是延續之前的文章 【C#多工作業與平行處理技術講解】,講解與實作如何在利用強大的 透過 Parallel 類別支援的方法,撰寫非同步任務,這裡一樣我會透過簡單程式碼進行實作教學。


   在並行任務與建立一個新的執行緒過程來執行一個非同步作業的操作是非常相似地,但是在這邊的並行任務卻是可以更好地發揮多個CPU處理器或者多核心處理器的強大優勢,進而充分地利用CPU的可用資源來執行非同步作業任務,更方便地是,在 .Net 並行任務的操作過程被封裝地很好,身為程式開發人員只需要調用呼叫對應的類別與相關成員方法就可以很輕鬆地建立與執行並行任務了,至於任務是在那個CPU處理器執行,操作系統的底層會自動進行調用配置。


    在這邊我使用一個 WPF 的專案範例進行講解,來說明如何利用 .Net 的 Parallel 類別執行非同步作業。


步驟一 :

在 Visual Studio 2017 建立一個 WPF 專案,

wpf


步驟二:

在 主要的 MainWindow.xaml 畫面,用xaml 語法宣告了一下界面結果


2017-07-08_17-12-48


主要是有 提供 3 個讓使用者輸入整數值的 TextBox , 以及對應 3 個顯示執行結果的 TextBlock ,還有一個 Button (執行 Parallel 運算平行任務)的按鈕。


部分xaml 語法如下:

xaml

步驟三:

在 Button 的 Clieck 事件,進行撰寫主要的核心程式碼邏輯….

(Code)

private void OnClick(object sender, RoutedEventArgs e)
{
     int num1 = default(int);
     if (!int.TryParse(txtInput1.Text, out num1))
     {
         MessageBox.Show(“輸入第一個正整數值:");
         return;
     }
     int num2 = default(int);
     if (!int.TryParse(txtInput2.Text, out num2))
     {
         MessageBox.Show(“輸入第二個正整數值:");
         return;
     }
     int num3 = default(int);
     if (!int.TryParse(txtInput3.Text, out num3))
     {
         MessageBox.Show(“輸入第三個正整數值:");
         return;
     }


    //宣告第一個並行作業的委派實體
     Action act1 = () =>
         {
             int sum = 0;
             for (int x = 1; x <= num1; x++)
             {
                 sum += x;
             }


            //顯示執行結果
             this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => run1.Text = sum.ToString()));
         };


    //宣告第二個並行作業的委派實體
     Action act2 = () =>
     {
         int sum = 0;
         for (int x = 1; x <= num2; x++)
         {
             sum += x;
         }


        //顯示執行結果
         this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => run2.Text = sum.ToString()));
     };


    //宣告第三個並行作業的委派實體
     Action act3 = () =>
     {
         int sum = 0;
         for (int x = 1; x <= num3; x++)
         {
             sum += x;
         }


        //顯示執行結果
         this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => run3.Text = sum.ToString()));
     };



    //使用 Parallel 類別來執行 並行任務
     Parallel.Invoke(act1, act2, act3);


}


程式碼重點講解:

1.這邊我主要用了 3 個 Action 的委派實體,分別用來進行整數的累加運算任務。

2.使用了 Dispatcher.BeginInvoke() 方法,和Windows Form 專案一樣,WPF 在平行作業模式下,也分為主UI的主執行緒,和多個任務的各別執行緒,而在任務的執行緒要改變主執行緒控管的主畫面下,必須透過 Dispatcher.BeginInvoke ,在不會發生一個 UnauthorizedAccessException 例外錯誤。

3.也是最重要的程式碼邏輯,利用 Parallel類別的 Invoke 方法呼叫,分別傳入多個委派實體,在這邊我宣告了3個 Action 委派實體,我一次傳入 Parallel.Invoke(act1,act2,act3), 這樣程式就自動進行3個平行作業任務了。


參考文章
C#多工作業與平行處理技術講解
1.透過 Thread 類別撰寫多執行緒多工作業。
2.透過 Delegate 委託支援的方法,撰寫非同步任務。
3-1.透過 Parallel 類別操控多任務平行作業(一)。
3-2. 透過 Parallel 類別操控多任務平行作業(二)。
4.使用 Task 自行控制非同步任務作業。
5.在非同步作業時,如何取消非同步任務。
6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。
7.非同步存取變數的問題。
8.非同步資源鎖定解決方式。



網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen

透過 Delegate 委託支援的方法,撰寫非同步任務(軟體開發、客製化軟體、系統設計)

      此篇文章主要是延續之前的文章 【C#多工作業與平行處理技術講解】,講解與實作如何在利用強大的 透過 Delegate 委託支援的方法,撰寫非同步任務,這裡一樣我會透過簡單程式碼進行實作教學。

       在.Net 平台的委派類型(Delegate)自身就可以很容易地使用非同步作業,因為只要是委派(Delegate)類型都一點有 BeginInvoke 以及對應的 EndInvoke() 這 2 個非同步操作方法,所以我們就可以非常容易地透過 委派實體 直接 使用非同步任務。

      在這邊我一樣使用 Windows Form 來做示範(因為比較單純可以講解程式技巧,所以不要問我為何不用 ASP.NET MVC),整個實作範例畫面為如下圖:


繪圖


步驟1 :

在 Visual Studio 新增一個 Windows Form 專案,我們準備拖拉幾個控制箱來達成 主要結果畫面,分別有

Label 控制箱 (Text = 基數)、Button 按鈕控制箱(Text = 進行計算作業 , Name = btnDelegateInvoke)、ProgressBar控制箱(用於顯示非同步作業的進度)、TextBox 文字方塊(Name = txtResult , 用於顯示執行計算結果)。

步驟 2:

在 Button 控制箱 (name = btnDelegateInvoke)的 Click 事件,撰寫主要核心代碼:

private void btnDelegateInvoke_Click(object sender, EventArgs e)
{
     int baseNum = default(int);
     if (!int.TryParse(txtBaseNum.Text, out baseNum))
     {
         MessageBox.Show(“請輸入一個正整數哦!");
         return;
     }


    txtResult.Clear();


    // 顯示進度表的狀態
     IProgress<int> progressReporter = new Progress<int>((p) =>
     {
         this.progressBar1.Value = p;
     });


    //  計算階乘的委派實體
     Func<int, BigInteger> ComputeNumValueAction = (bsNum) =>
         {
             BigInteger bi = new BigInteger(1);
             for (int i = 1; i <= bsNum; i++)
             {
                 bi *= i; //相乘
                 // 用於計算目前進度
                 double ps = Convert.ToDouble(i) / Convert.ToDouble(bsNum) * 100d;
                 progressReporter.Report(Convert.ToInt32(ps));
             }
             return bi;
         };


    // 開始呼叫使用
     btnDelegateInvoke.Enabled = false;
     ComputeNumValueAction.BeginInvoke(baseNum, new AsyncCallback(FinishedCallback), ComputeNumValueAction);


}


2017-07-06_01-01-56


程式碼邏輯講解:

1.在 Progress<T> 是實現 IProgress<T> 介面,所以這裡可以透過它支援的 Report()方法來報告目前非同步的操作進度數據,該對象在進度更新後,是允許使用者直接繼續操作主畫面的,例如拖拉。


2.Func<int,BigInteger> 委派,代表自身帶有一個 int 類型的參數,而返回的值類型為 BigInteger,這邊我特別用 BigInteger 類型,因為在計算階乘的計算結果,可能數字會非常大,會超出 long 類型的最大值。


3.在開始進行使用 委派(Delegate)支援的BeginInvoke方法時,必須再使用一個 AsyncCallback 委派,該委派主要是用於綁定一個方法,在檔非同步任務完成時,可以呼叫的回調方法,並且再回調方法中,再次調用 Delegate.EndInvoke方法來捕捉計算結果。


我們直接看 AsyncCallback 綁定的 FinishedCallback 方法。


private void FinishedCallback(IAsyncResult ar)
{
     // 取出委派變數
     Func<int, BigInteger> action = (Func<int, BigInteger>)ar.AsyncState;
     // 求得計算結果
     BigInteger res = action.EndInvoke(ar);
     this.BeginInvoke(new Action(() =>
     {
         btnDelegateInvoke.Enabled = true;
         // 顯示計算結果
         txtResult.Text = string.Format(“計算結果:{0}", res);
     }));
}


ddd



上述程式碼撰寫完畢後,就可以編譯執行,在 基數欄位輸入一個正整數,就可以進行非同步的計算階乘作業。





參考文章

C#多工作業與平行處理技術講解

1.透過 Thread 類別撰寫多執行緒多工作業。

2.透過 Delegate 委託支援的方法,撰寫非同步任務。

3.透過 Parallel 類別操控多任務平行作業。

4.使用 Task 自行控制非同步任務作業。

5.在非同步作業時,如何取消非同步任務。

6.多執行緒多任務存取相同變數,但卻各自隔離保留各自任務的值。

7.非同步存取變數的問題。

8.非同步資源鎖定解決方式。





網智數位-軟體開發(軟件開發)

針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna

黃先生 Allen

     

在 .Net 透過 Thread 類別撰寫多執行緒多工作業【軟體開發、軟件開發、客製化軟體】

     此篇文章主要是延續上一篇 【C#多工作業與平行處理技術講解】,講解與實作如何在利用強大的 Thread 類別,使用 C# 來撰寫多執行緒多工作業功能。

在 .Net 平台中 Thread 類別位於 System.Threading 命名空間中,此類別封裝了多執行緒許多方法與功能,可以讓我們快速建立新的執行緒,進而在新的執行緒執行所需要的程式碼邏輯,在我們建立一個 Thread 的實例(Instance)時,需要透過一個委派(Delegate)與新建立的 Thread 實例和一個現有的方法(Method)進行綁定,當執行緒(Thread)啟動後,就會立即執行這個綁定的方法。

傳遞給 Thead 的建構子的委派(Delegate)有 2 種,一種是不帶任何參數的委派方法;第二種是可以帶一個 object 參數的委派方法。在我們實體化 Thread 物件後,就可以開始調用 Thread.Start()方法,來啟動執行緒(也會開始執行綁定的方法),而在執行過程隨時可以調用 Thread.Abort()方法,來強制終止執行緒的執行,不過這個 Abort()方法會引發一個 ThreadStateException 例外錯誤。

現在我就帶一個實際範例來是示範如何 用 C# 使用 Thread 類別,建立一個執行緒作業,這邊為了簡單講解觀念,我使用最簡單的 Windows Form 專案來講解。

Step 1. 透過 Visual Studio 2017(舊版本 2015/2013/2012都行啦)新增一個 Windows Form 專案

2017-07-02_23-33-29

Step 2.在 Step 1建立的 Windos  Form 專案後,會產生一個 Form1的界面表單,打開此表單,然後從工具箱拖拉一個 ProgressBar 控制箱到 Form1 畫面,並排好版面。

step2

     Step 3. 從工具箱在拖拉一個 Button 到 Form1 的畫面,並將 name 屬性命名為 btnTread , Text 修改為【Thread 執行緒 啟動】step3

 Step 4. 在 Form1.cs 後置程式碼裡,我撰寫了一個 DoWok() 方法,裡面撰寫了一下邏輯,程式碼如下:

/// <summary>
/// 此方法將會透過委派(Delegate)指定給新的執行緒(Thread)
/// </summary>
private void DoWork()
{
  int p = 0;

  while (p < 100)
  {
    p++;
    Thread.Sleep(100);

    //更新進度表
    this.BeginInvoke(new Action(() =>
    {
       this.progressBar1.Value = p;
    }));
  }

  this.BeginInvoke(new Action(delegate ()
  {
   //再次啟用按鈕狀態
   btnTreadStart.Enabled = true;
   //重設定進度表為 0
   progressBar1.Value = 0;
   //顯示執行緒已完成
   MessageBox.Show(“執行緒已完成。", “執行緒已完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
  }));

}

step4

Step 5. 在 Form1 的 Button 按鈕,雙擊滑鼠 (button1_Click),在button1_Click 事件我撰寫了 核心的 操控 Thread 程式碼

private void button1_Click(object sender, EventArgs e)
{

  //宣告一個 Thread 類別,並將在傳入 Step 4 所宣告的 DoWord 方法
  //待會 Thead 執行時,就會立即執行 DoWord 方法
  Thread newThread = new Thread(DoWork);

  //停用按鈕
  btnTreadStart.Enabled = false;

  //啟用新的執行緒
  newThread.Start();

}

step5

Step 6. Compile 編譯執行(執行F5),就可以看到結果

step 5-1

step 5-2

 

程式碼補充解釋:
因為基於執行緒的安全考量和包含使用者界面完整性,一般而言,系統是限制跨執行緒去更新修改使用者界面,所以在 Step 4 的 DoWokd方法,我透過呼叫 this.BeginInvoke() 方法傳遞一個 委派,使得可以在使用者界面的主執行緒上面進行更新使用者界面的控制箱(ProgressBar1),所以可以看到進度表一直不斷更新。

透過這一篇文章希望可以說明如何利用 Thread 類別來操作多執行作業,後續我還會抽出時間講解不同的非同步多工作業方式,分成不同的文章來一一描述。

 

 

網智數位-軟體開發(軟件開發)
針對各特殊產業都可以量身定做符合貴公司的需求,別人無法克服的就是我們的挑戰
業務合作、軟體委外開發
業務窗口:allen@netqna.com
聯繫電話:0920-883-870
公司電話:02-55991310
公司地址(業務營運處):台北市中山區錦州街 25 號 5 樓
skype: netqna
line:netqna
微信:netqna
黃先生 Allen