[C#] 多线程总结(结合进度条)

摘要:
(1) 使用线程的原因1。线程可用于将代码与其他代码隔离,并提高应用程序的可靠性。当所有前台线程关闭时,所有后台线程也将直接终止,而不会引发异常。

(一)使用线程的理由

1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。

2、可以使用线程来简化编码。

3、可以使用线程来实现并发执行。

(二)基本知识

1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源。

2、前台线程和后台线程:通过Thread类新建线程默认为前台线程。当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。

3、挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程序的执行情况不可预知,所以使用挂起和唤醒容易发生死锁的情况,在实际应用中应该尽量少用。

4、阻塞线程:Join,阻塞调用线程,直到该线程终止。

5、终止线程:Abort:抛出 ThreadAbortException 异常让线程终止,终止后的线程不可唤醒。Interrupt:抛出 ThreadInterruptException 异常让线程终止,通过捕获异常可以继续执行。

6、线程优先级:AboveNormal BelowNormal Highest Lowest Normal,默认为Normal。

(三) 线程生命周期

  1. 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
  2. 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
  3. 不可运行状态死亡状态:当线程已完成执行或已中止时的状况。
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞

(四) Thread 常用方法:

  • public void Interrupt()    中断处于 WaitSleepJoin 线程状态的线程。
  • public void Join()         在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。
  • public void Start()        开始一个线程
  • public static void Sleep(int millisecondsTimeout)    让线程暂停一段时间

一 普通线程

分为两种,一种是不需要给子线程传参数,Thread t = new Thread(new ThreadStart(void () target)); 另一种是要给子线程传一个参数,Thread t = new Thread(new ParameterizedThreadStart(void (object) target));

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第2张
// 普通线程
private void btn1_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    Thread tt = new Thread(new ThreadStart(DoWork1));
    tt.Name = "不带参数普通线程";
    tt.Start();
    Thread t = new Thread(new ParameterizedThreadStart(DoWork2));
    t.Name = "带参数普通线程";
    t.IsBackground = true;
    t.Start(100);
    _msg += "当前线程的执行状态:" + t.IsAlive + "
";
    _msg += "当前托管线程的唯一标识:" + t.ManagedThreadId + "
";
    _msg += "线程名称:" + t.Name + "
";
    _msg += "当前线程的状态:" + t.ThreadState;
    MessageBox.Show("消息:
" + _msg, "提示", MessageBoxButtons.OK);
}
// 线程方法
private void DoWork1()
{
    for (int i = 0; i < 100; i++)
    {
        // 跨线程访问 UI,BeginInvoke 采用异步委托
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
// 线程方法
private void DoWork2(object obj)
{
    for (int i = 0; i < (int)obj; i++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
普通线程

二  线程池

public static bool QueueUserWorkItem(WaitCallback);

public static bool QueueUserWorkItem(WaitCallback, object);

线程池默认为后台线程(IsBackground)

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第4张
private void btn3_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(DoWork2, 100);
    // 或者
    ThreadPool.QueueUserWorkItem((s) =>
    {
        int minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, maxCompletionPortThreads;
        ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
        ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
        MessageBox.Show(String.Format("WorkerThreads = {0} ~ {1}, CompletionPortThreads = {2} ~ {3}",
            minWorkerThreads, maxWorkerThreads, minCompletionPortThreads, maxCompletionPortThreads));
        DoWork2(100);
    });
}
// 线程方法
private void DoWork2(object obj)
{
    for (int i = 0; i < (int)obj; i++)
    {
        // Thread.Sleep(50);
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
线程池

三  BackgroundWorker

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第6张
private void btn4_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = true;// 是否报告进度更新
    // 线程执行
    bw.DoWork += new DoWorkEventHandler((obj, args) =>
    {
        for (int i = 0; i < 100; i++)
        {
            bw.ReportProgress(i);
        }
    });
    // UI主线程显示进度
    bw.ProgressChanged += (obj, progressChangedEventArgs) =>
    {
        progressBar.Value = progressChangedEventArgs.ProgressPercentage;
    };
    // 线程执行完成后的回调函数
    bw.RunWorkerCompleted += (obj, runWorkerCompletedEventArgs) =>
    {
        MessageBox.Show("子线程执行完成!");
    };
    if (!bw.IsBusy)
    {
        bw.RunWorkerAsync();
    }
}
BackgroundWorker

三  Task(.NET 4.0以上版本)

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第8张
private void btn5_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    Task<bool> t = new Task<bool>(maxValue => DoWork((int)maxValue), progressBar.Maximum);
    t.Start();
    t.Wait();
    // 任务完成后继续延续任务
    Task cwt = t.ContinueWith(task => MessageBox.Show("The result is " + t.Result));
}
// 线程方法
private bool DoWork(int maxValue)
{
    for (int n = 0; n < maxValue; n++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = n;
        }), null);
    }

    return true;
}
Task

四  异步委托

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第10张
public delegate string MyDelegate(object arg);

private void btn6_Click(object sender, EventArgs e)
{
    MyDelegate myDelegate = new MyDelegate(DoWork3);
    IAsyncResult result = myDelegate.BeginInvoke(100, DoWork2Callback, "回调函数参数");

    // 异步执行完成
    string resultStr = myDelegate.EndInvoke(result);
}

// 线程函数
private string DoWork3(object arg)
{
    for (int n = 0; n < (int)arg; n++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = n;
        }), null);
    }

    return "Finished";
}

// 异步回调函数
private void DoWork2Callback(IAsyncResult arg)
{
    MessageBox.Show(arg.AsyncState.ToString());
}
异步委托

五  附 跨线程访问UI之 SynchronizationContext (同步上下文)

[C#] 多线程总结(结合进度条)第1张[C#] 多线程总结(结合进度条)第12张
private void btn2_Click(object sender, EventArgs e)
{
    SynchronizationContext context = SynchronizationContext.Current;
    new Thread(() =>
    {
        for (int i = 0; i < 100; i++)
        {
            // Send方法是发送一个异步请求消息
            //context.Send((s) =>
            //{
            //    progressBar.Value = i;
            //}, null);
            // Post方法是发送一个同步请求消息
            context.Post((s) =>
            {
                progressBar.Value = i;
            }, null);
        }
    }).Start();
}
SynchronizationContext

 

六  Task+事件+控制(暂停、继续):

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TaskTest
{
    public partial class Form1 : Form
    {
        public event EventHandler<MyEventArgs> MyNotify;
        private delegate void DelegateSetProgress(MyEventArgs e);
        CancellationTokenSource tokenSource;
        CancellationToken token;
        ManualResetEvent reset;

        public Form1()
        {
            InitializeComponent();
            MyNotify += new EventHandler<MyEventArgs>(GetProgress);            
        }

        private void SetProgress(MyEventArgs e)
        {
            if(progressBar1.InvokeRequired)
            {
                Invoke(new DelegateSetProgress(SetProgress), e);
            }
            else
            {
                progressBar1.Value = e.value;
                txtMessage.AppendText(e.text);
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            reset = new ManualResetEvent(true); // 初始化为true时执行WaitOne不阻塞
            MyNotify(null, new MyEventArgs() { value = 0, text = $"程序已经启动
" });
            Task.Run(() =>
            {
                for (int i = 1; i <= 100; i++)
                {
                    if (token.IsCancellationRequested) return;
                    reset.WaitOne();
                    MyNotify(null, new MyEventArgs() { value = i, text = $"进度为:{i}/100 
" });
                    Thread.Sleep(100);
                }
            },token);
        }

        private void GetProgress(object sender,EventArgs e)
        {
            SetProgress(e as MyEventArgs);
        }

        private void btnPause_Click(object sender, EventArgs e)
        {
            btnPause.Text = btnPause.Text == "暂停" ? "继续" : "暂停";
            if (btnPause.Text == "暂停")
                reset.Set();
            else
                reset.Reset();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            tokenSource.Cancel();
        }
    }

    public class MyEventArgs : EventArgs
    {
        public int value = 0;
        public string text = string.Empty;
    }
}

 

七  参考资料:

☆参考博客 http://www.cnblogs.com/luxiaoxun/p/3280146.html

☆多线程讲解 http://www.w3cschool.cc/csharp/csharp-multithreading.html

免责声明:文章转载自《[C#] 多线程总结(结合进度条)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇hive SQL 字母大小写转换python(3)- 循环语句:从最内层跳出多层循环下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

RxJava入门

项目小版本上线,抽空简单学习了下久仰大名的RxJava 一、引入 个人觉得rxjava的特点: 强大灵活的事件流处理(多线程/多事件/复合对象) 强大灵活优雅简洁的异步 链式调用 可自动Lambda化   实现:RxJava 是通过一种扩展的观察者模式来实现的 类比 类比 实际 实际 职责 演讲者 Button (可)被订阅者 (同右)...

web 阶段的一些简答题

1.jsp 9个隐含对象 2. jsp 4大域对象 3.mybatis 中 #{} %{ } 的区别于联系 4. Servlet容器默认是采用单实例多线程的方式处理多个请求的: 5.Cookie 与Session 的异同 6. 请描述对web 服务器的理解(Tomcat),请列举出tomcat7下的目录以及各个目录的作用 7. 请描述 servlet与st...

深度剖析OpenGL ES中的多线程和多窗口渲染技术

由 创新网小编 于 星期五, 2014-04-11 14:56 发表 移动设备中的CPU和GPU已经变得很强大,到处都是配备一个或多个高分辨率屏幕的设备,需要使用带有图形驱动器的复杂交互也日益增加。在这篇博客文章中,我将讨论多线程和多窗口渲染对开发人员来讲意味着什么,同时我将介绍将这些技术应用您设计当中的条件和时机。 什么是多线程渲染? 传统上,Ope...

Java_如何等待子线程执行结束

工作中往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总的产景, 这时候就需要使用线程了. 一个线程启动之后, 是异步的去执行需要执行的内容的, 不会影响主线程的流程,  往往需要让主线程指定后, 等待子线程的完成. 这里有几种方式. 站在 主线程的角度, 我们可以分为主动式和被动式. 主动式指主线主动去检测某个标...

【转】编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型

建议87:区分WPF和WinForm的线程模型WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(如Button、TextBox等)必须由创建它的那个线程进行更新。WinForm在这方面的限制并不是很严格,所以像下面这样的代码,在WinForm中大部分情况下还能运行(本建议后面会详细解释为什么会出现这种现象): private void but...

java多线程读取、操作List集合

import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.ArrayUtils;   public class Test_4 {     /**      * 多线程处理list      *       * @param ...