一、概述:C#支持多线程并行执行程序,一个线程有他单独的执行路径,能够与其他线程同时执行,一个程序是由一个单线程开始,该单线程由CLR(公共语言运行时)和操作系统创建而成,并具有多线程创建额外线程的功能。

1、创建线程的方法

<1>、通过Thread类来创建线程,ThreadStart委托创建线程从哪里开始运行的方法

using System;
using System.Threading;
namespace Mulithreading{
class CreateThreadMethods{
static void Main(string[] args){
ThreadStart ts=new ThreadStart(Run);//创建指定线程从哪里(哪个方法)开始的委托
Thread th=new Thread(ts);//传入指定的委托,创建线程实例
th.Start();//开始线程 }
static void Run(){ }
}
}

<2>第二种方式:通过C#语法糖来创建线程,直接给Thread类传递方法,这个方法会被编译器自动推断出来,也就是说只要传递给Thread类的方法符合ThreadStart委托所定义的方法格式,那么这个方法会被编译器自动编译成ThreadStart委托

using System;
using System.Threading;
namespace Mulithreading{
class CreateThreadMethods{
static void Main(string[] args){
Thread th=new Thread(Run);
th.Start();
}
static void Run(){
}
}
}

<3>第三种方式:采用匿名委托的方式,创建线程

static void Main() {
Thread t = new Thread (delegate() { Console.WriteLine ("Hello!"); });
t.Start();
}

2、创建线程前需要注意的事项

<1>、线程有一个IsAlive属性,在调用Start()之后直到线程结束之前一直为true。一个线程一旦结束便不能重新开始了。

二、实例

1、主线程和子线程分别执行不同的任务

using System;
using System.Threading;
namespace MuliThreading
{
class Thread1
{
static void Main(string[] args)
{
Thread t = new Thread(writeY); //为该类传入一个方法(委托)
t.Start();
while (true) Console.Write("x");
}
static void writeY()
{
while (true) Console.Write("y");
} //代码解读:主线程创建了一个新线程t,t传入的是writeY方法,重复打印y,同时主线程打印x,主线程和新线程同时执行 }
}

输出结果:

无限输出x和y;

2、主线程和子线程分别执行相同的任务

using System;
using System.Threading; namespace Mulithreading
{
class Thread2
{
static void Main(string[] args) {
new Thread(Go).Start();
Go();
}
static void Go() {
for (int i = ; i < ; i++) {
Console.Write("?");
}
}
//代码解读:在主线程中创建了一个子线程,主线程和子线程同时执行Go()
}
}

输出:

3、主线程和子线程使用同一目标的公共实例

using System;
using System.Threading; namespace Mulithreading
{
class Thread3
{
bool done;
static void Main(string[] args)
{
Thread3 t3 = new Thread3();
Thread t = new Thread(t3.Go);
t.Start();
t3.Go(); }
void Go() {
if (!done) { done = true; Console.Write("done"); }
} //代码解读:主线程Main()方法和在其中定义的子线程所调用的GO()方法共享以一个公共属性done,当吊用子线程时,对done的修改会影响到主线程的使用,因为两个线程在理论上讲是同时执行,但是实际上不可能精确的同时执行,所以当主线程吊用Go()方法是done为true
}
}

输出:done

4、主线程和子线程使用同一目标属性可能会出现的问题

using System;
using System.Threading;
namespace Mulithreading
{
class Thread4
{
bool done;
static void Main(string[] args)
{
Thread4 t4 = new Thread4();
new Thread(t4.Go).Start();
t4.Go();
}
void Go()
{
if (!done) { Console.Write("done"); done = true; }
}
//代码解读:当在主线程中吊用子线程时,注意两个线程是同时进行的,所以当子线程吊用Go()方法并执行时,主线程也同时进行吊用执行,两个线程是并行的,所以他们同时输出了done
}
}

输出:done  done

5、线程间共用同一静态变量产生的"线程安全问题"

using System;
using System.Threading; namespace Mulithreading
{
class Thread5
{
//静态字段提供了另一种线程间共享数据的方法
static bool done;
static void Main(string[] args)
{
new Thread(Go).Start();
Go();
}
static void Go() {
if (!done) { done = true; Console.Write("done"); }
}
//代码解读:这个demo充分的说明了一个潜在的问题,"线程安全问题",从这这列子中看就是,无法确定输出的结果,有可能是输出两个"done"(虽然可能性不大),但是如果调正代码如下
//static void Go(){
//if(!done){Console.Write("done");done=true;}
//}
//这样打印两次done的概率就大大增大了 }
}

输出:done

6、使用排他锁(locker)解决线程安全问题

使用场景:在多线程编程中,会有多个线程并发吊用同一个代码块A的情况,用来提升代码的执行效率。在某些情况下,我们可能需要在执行代码块A的同时,同步地执行代码块B。即同一个时间段只有一个线程执行代码块B,这个时候就需要用到排他锁(lock),lock能确保代码块B完成运行的同时,保证不会被其他线程所干扰或中断。它可以把一段代码定义成为互斥段,互斥段在一个时刻内只允许一个线程进入,其他线程必须等待,下面是实例代码:

using System;
using System.Threading;
namespace Mulithreading
{
class Thread6
{
static bool done;
static Object locker = new Object();
static void Main(string[] args)
{
new Thread(Go).Start();
Go();
}
static void Go()
{
lock (locker)
{
if (!done) { Console.Write("done"); done = true; }
}
}
}
}

注意:

1、lock语句中的表达式一定要是引用类型的表达式,编译器永远不会为lock内的语句进行隐式装箱转换,当lock内的语句为值类型而不是引用类型时,则会报一个编译错误;

2、常用的引用类型有:类、接口、委托、字符串、object、数组。但是最好不要锁定字符串,因为使用lock进行同步时,要保证lock的是同一个对象,当我们对lock的字符串进行赋值(修改)是,实际上是创建了一个新的对象,这样多个线程以及每个循环之间所lock的对象都不同,这样达不到同步的效果,常用的方法是,new  一个Object,并且永不修改他。

using System;
using System.Threading; namespace Mulithreading
{
class CastCoin
{
static Object locker = new Object();
public static int donationNums = ;
static void Main(string[] args) {
new Thread(Write).Start();//先进
new Thread(Write).Start();//后进
}
static void Write() {
while (true)
{
lock (locker)
{
donationNums += ;
Console.WriteLine("当前有人正在捐款......请稍等");
Thread.Sleep();
Console.WriteLine("到目前为止,共募捐{0}元", donationNums);
}
}
}
}
}

代码解读:上面代码加了lock之后,一次只允许一个线程进入,所以输出结果如下图:

如果不加lock,结果如下图:

分析:两个线程同时执行累加100的操作,所以当输出总金额的时候,两个线程都执行完了累加100的操作,所以两个线程都输出200......以此类推;

7、使用Join方法,阻塞调用线程,直到子线程终止或者到一定的时间为止

Join一共有三个重载方法

 public void Join();
public bool Join(int millisecondsTimeout);
public bool Join(TimeSpan timeout);

相信理解了一个,其他的就迎刃而解了。

首先说Join()方法,他是Thread类的一个实例方法,可惜的是我大MSDN对于Join()的注释也太简单了,下面的英文有看不懂,所以只能去问度娘了.

MSDN:在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止。

度娘:直接上代码

using System;
using System.Threading;
using System.Diagnostics;
namespace Mulithreading{
class Thread_Join{
static void Main(string[] args){
Stopwatch sw=Stopwatch.StartNew();
Thread[] ths=new Thread[];
Array.ForEach<Thread>(ths,t=>{
t=new Thread(new ThreadStart(Run));//开启一个新的时间间隔实例
t.Start();
t.Join();//阻塞主线程,直到线程数组执行完毕才会执行主线程中的方法
});
Console.Write("总共花费时间:{0}",sw.Elapsed);
}
static void Run(){
Thread.Sleep();
}
}
}

输出:

推论:根据结果可以推断线程数组依次去执行Run()方法,如果不是依次,那么输出结果就会是1秒。而且t.Join让子线程依次执行Run()方法的同时,也阻塞了主线程。

综上所述:也就是主线程执行子线程数组,子线程数组在Join()方法的影响下,子线程数组中的子线程不会并发的一次性全部执行完毕,而是一个个依次执行,而主线程(这里是Main()方法),只有当子线程数组全部执行完毕,才会执行主线程中的方法;

下面我们通过代码来证明这一推论:

using System;
using System.Threading;
using System.Diagnostics;
namespace Mulithreading
{
class Thread_Join
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Thread[] ths = new Thread[];
int i = ;
Array.ForEach<Thread>(ths, t =>
{
t = new Thread(new ThreadStart(Run));
t.Start();
i++;
t.Name = "线程" + i; //给当前线程命名
t.Join();
});
Console.WriteLine("总共花费时间:{0}", sw.Elapsed);
sw.Stop();
}
static void Run()
{
Thread.Sleep();
Console.WriteLine("当前正在执行的线程:{0}", Thread.CurrentThread.Name);//输出正在执行该方法的线程名
}
}
}

通过给线程命名的方式,证明了我们的推论是正确的;

8、通过ParameterizedThreadStart委托给线程调用的方法传递参数来区分线程

using System;
using System.Threading; namespace Mulithreading
{
class ParameterizedThreadStart_Study
{
static object locker = new object();
static void Main() {
//由于ThreadStart只能接收无参数无返回值的方法,但是有些时候我们需要为线程吊用的方法传递对象,这个时候ThreadStart就无法完成这个工作
//所幸的是.Net Framework定义了另一个版本的委托叫做PatameterizedThreadStart,单从字面上看它的意思是:参数化的ThreadStart,它可以接收一个Object作为参数 ParameterizedThreadStart pt = new ParameterizedThreadStart(Run);//ParameterizedThreadStart接收一个方法(该方法无返回值,接收一个Object作为参数)
Thread th = new Thread(pt);
th.Start(true);
Run(false); }
static void Run(object obj){
lock (locker)
{
if (obj != null)
Console.WriteLine((bool)obj == true ? "hello" : "HELLO");
else
Console.WriteLine("Run方法需要一个object参数");
}
}
}
}

C#多线程学习一的更多相关文章

  1. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  2. 【转】C#多线程学习

    C#多线程学习(一) 多线程的相关概念 什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序 ...

  3. java多线程学习

    在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口. 一.扩展java.lang.Thread类 package com.multithread.lea ...

  4. Java多线程学习(吐血超详细总结)

    本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的 ...

  5. C#多线程学习(一) 多线程的相关概念(转)

    什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄 ...

  6. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

  7. C#多线程学习之(五)使用定时器进行多线程的自动管理

    本文实例讲述了C#多线程学习之使用定时器进行多线程的自动管理.分享给大家供大家参考.具体分析如下: Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个新的线程,执 ...

  8. [转] Qt 多线程学习

    Qt 多线程学习 转自:http://www.cnblogs.com/IT-BOY/p/3544220.html 最近的项目上用到了关于多线程的知识,自己也比较感兴趣,所以就拿了那本<C++ G ...

  9. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  10. C#多线程学习(一) 多线程的相关概念

    什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄 ...

随机推荐

  1. Log4Net配置使用简记

    1,引用Log4Net.dll .当前为2.0.8.0版,可添加Nuget包.我的办法是从下载的包中直接引用相应.net版本的dll以减小项目体积 2,在App.config中增加<sectio ...

  2. Jboss,Tomcat 远程调试配置

    Jboss,Tomcat 远程调试配置 2007-12-25 15:51:01|  分类: 默认分类|字号 订阅   Eclipse远程调试JBoss应用 修改JBoss启动配置            ...

  3. 第二届CCCC赛后感想 2017-04-15 23:56 88人阅读 评论(0) 收藏

    第一次写赛后感想,也不算什么很正规的比赛,不过这次比赛的时间恰好处于思想变化的阶段,留贴纪念. 先谈谈这次比赛,弱校萌新,依靠申请进了总决赛,发现和第一届不一样,缺少了团队奖心中有点缺乏动力,比赛2个 ...

  4. FTP服务器的搭建与安全配置

    FTP可以说是Internet上使用非常广泛的一种通讯协议了.它工作在OSI模型的第7层,是TCP/IP的一种具体应用.FTP采用基于TCP的可靠连接:监听21端口来等待控制连接请求,当连接建立后,采 ...

  5. java向数据库插入N条数据

    为了测试mysql的索引,要向数据库先插入上万条数据,然后再测试.手动插入太麻烦,写了一段代码. 先上代码: package action; import java.sql.Connection; i ...

  6. ssl协议,openssl,创建私有CA

    SSL是Security Socket Layer:安全的套接字层 他介于HTTP和TCP协议层之间 SSL是Netscape公司开发的,属于个人 TLS是标准委员会制定的 OpenSSL是SSL的开 ...

  7. 搭建 .NET Core 开发环境

    安装 .Net Core 执行代码 任务时间:时间未知 .NET Core 的官方文档很详细,本实验带你建立一个.NET Core 1.1的Web运行环境,更多内容可以可以查阅微软官方文档. 安装 . ...

  8. asp.net mvc部分视图的action中获取父级视图信息

    RouteData.DataTokens["ParentActionViewContext"]中包含了父级视图的相关信息,如路由等 public ActionResult Chil ...

  9. ASP.NET Core学习指导

    ASP.NET Core 学习指导 "工欲善其事必先利其器".我们在做事情之前,总应该做好充分的准备,熟悉自己的工具.就像玩游戏有一些最低配置一样,学习一个新的框架,也需要有一些基 ...

  10. 关于StreamReader.ReadToEnd方法

    以前写抓取网页的代码喜欢用ReadToEnd,因为简单省事,后来发现,在爬取网页的时候,如果网速很慢,ReadToEnd超时的几率很大.使用Read改写后,超时几率大大减小,完整代码如下: /// & ...