C#学习(七)- Tread

1. 概述与概念
  C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。这里的一个简单的例子及其输出:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Thread_practice
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(WriteY);
            t.Start();                          // Run WriteY on the new thread
            while (true) Console.Write("x");   // Write ‘x‘ forever
        }

        static void WriteY()
        {
            while (true) Console.Write("y");   // Write ‘y‘ forever
        }
    }
}

程序将不停的输出x和y,且输出x的程序和输出y的程序在不同线程同时运行,因此输出x和y没有次序。输出结果:

  进程之间对于各自内部的变量互不干扰,如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Thread_practice
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(Write);
            t.Start();
            Write();
        }

        static void Write()
        {
            for (int cycles = 0; cycles < 50; cycles++) Console.Write("cycles="+cycles+"\n");
        }
    }
}

输出结果:

变量cycles的副本分别在各自的内存堆栈中创建,输出也一样。线程们还可以引用一些公用的目标实例的时候,他们会共享数据。下面是实例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Thread_practice
{
    class Program
    {
        static bool done = true;

        static void Main(string[] args)
        {
            Thread t = new Thread(Write);
            t.Start();
            Thread.Sleep(1000);    //让主进程暂停一秒,删除这句可能会由于主进程运行                     //太快而没有来得及察觉到进程t对done值的修改而导致两次输出”it is done!“
            Write();
        }

        static void Write()
        {
            if (done)
            {
                Console.Write("it is done!\n");
                done = false;
            }
        }
    }
}

输出结果:

输出一个”it is done!“,而不是两个。

关于线程的几个讨论:

线程 vs. 进程

  属于一个单一的应用程序的所有的线程逻辑上被包含在一个进程中,进程指一个应用程序所运行的操作系统单元。

  线程于进程有某些相似的地方:比如说进程通常以时间片方式与其它在电脑中运行的进程的方式与一个C#程序线程运行的方式大致相同。二者的关键区别在于进程 彼此是完全隔绝的。线程与运行在相同程序其它线程共享(堆heap)内存,这就是线程为何如此有用:一个线程可以在后台读取数据,而另一个线程可以在前台展现已读取的数据。

何时使用多线程

  多线程程序一般被用来在后台执行耗时的任务。主线程保持运行,并且工作线程做它的后台工作。对于Windows Forms程序来说,如果主线程试图执行冗长的操作,键盘和鼠标的操作会变的迟钝,程序也会失去响应。由于这个原因,应该在工作线程中运行一个耗时任务时 添加一个工作线程,即使在主线程上有一个有好的提示“处理中...”,以防止工作无法继续。这就避免了程序出现由操作系统提示的“没有相应”,来诱使用户 强制结束程序的进程而导致错误。模式对话框还允许实现“取消”功能,允许继续接收事件,而实际的任务已被工作线程完成。BackgroundWorker恰好可以辅助完成这一功能。

  在没有用户界面的程序里,比如说Windows Service, 多线程在当一个任务有潜在的耗时,因为它在等待另台电脑的响应(比如一个应用服务器,数据库服务器,或者一个客户端)的实现特别有意义。用工作线程完成任务意味着主线程可以立即做其它的事情。

  另一个多线程的用途是在方法中完成一个复杂的计算工作。这个方法会在多核的电脑上运行的更快,如果工作量被多个线程分开的话(使用Environment.ProcessorCount属性来侦测处理芯片的数量)。

  一个C#程序称为多线程的可以通过2种方式:明确地创建和运行多线程,或者使用.NET framework的暗中使用了多线程的特性——比如BackgroundWorker类, 线程池threading timer, 远程服务器,或Web Services或ASP.NET程序。在后面的情况,人们别无选择,必须使用多线程;一个单线程的ASP.NET web server不是太酷,即使有这样的事情;幸运的是,应用服务器中多线程是相当普遍的;唯一值得关心的是提供适当锁机制的静态变量问题。

何时不要使用多线程

  多线程也同样会带来缺点,最大的问题是它使程序变的过于复杂,拥有多线程本身并不复杂,复杂是的线程的交互作用,这带来了无论是否交互是否是有意的,都会 带来较长的开发周期,以及带来间歇性和非重复性的bugs。因此,要么多线程的交互设计简单一些,要么就根本不使用多线程。除非你有强烈的重写和调试欲 望。

当用户频繁地分配和切换线程时,多线程会带来增加资源和CPU的开销。在某些情况下,太多的I/O操作是非常棘手的,当只有一个或两个工作线程要比有众多的线程在相同时间执行任务块的多。稍后我们将实现生产者/耗费者 队列,它提供了上述功能。

2. 创建和开始使用多线程

线程用Thread类来创建,通过ThreadStart委托来指明方法从哪里开始运行,调用Start方法后,线程开始运行,线程一直到它所调用的方法返回后结束。下面是一个例子,使用了C#的语法创建TheadStart委托:

class ThreadTest {
    static void Main() {
        Thread t = new Thread (new ThreadStart (Go));
        t.Start();   // Run Go() on the new thread.
        Go();        // Simultaneously run Go() in the main thread.
    }

    static void Go() { Console.WriteLine ("hello!"); }
}

一个线程可以通过C#堆委托简短的语法更便利地创建出来:

static void Main() {
    Thread t = new Thread (Go);    // No need to explicitly use ThreadStart
    t.Start();
    ...
}
static void Go() { ... }

在上面这种情况,ThreadStart被编译器自动推断出来,另一个快捷的方式是使用匿名方法来启动线程:

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

将数据传入ThreadStart中

  如果我们想将参数传入到新的进程中,该怎么办呢? 我们不能使用ThreadStart委托,因为它不接受参数,既不能这么写:

static void Main(string[] args)
{
    string inputNumber = Console.ReadLine();
    int a = Int32.Parse(inputNumber);

    Thread t = new Thread(Write(a));    //不能以这种方式传递参数到新进程
    t.Start();
    Write(a);   //main函数中可以这么写
}

static void Write(int cycle)
{
    for (int i = 0; i < cycle; i++)
    Console.WriteLine("hello!  i=" + i);
}

  所幸的是,.NET framework定义了另一个版本的委托叫做ParameterizedThreadStart, 它可以接收一个单独的object类型参数,所以想要传递参数到新进程,我们可以这样:

static void Main(string[] args)
        {
    string inputNumber = Console.ReadLine();
    int a = Int32.Parse(inputNumber);

    Thread t = new Thread(Write);
    t.Start(a);     //在这里传入参数
    Write(a);
}

static void Write(object obj)   //一定传入的是object对象
{
    int cycle = (int)obj;   //强制转换为int
    for (int i = 0; i < cycle; i++)
        Console.WriteLine("hello!  i=" + i);
}    
时间: 2024-10-04 08:22:11

C#学习(七)- Tread的相关文章

springMVC3学习(七)--Interceptor拦截器

Spring为我们提供了:org.springframework.web.servlet.HandlerInterceptor接口, org.springframework.web.servlet.handler.HandlerInterceptorAdapter适配器, 实现这个接口或继承此类,能够很方便的实现自己的拦截器. 有下面三个方法: Action之前运行 public boolean preHandle(HttpServletRequest request, HttpServletR

Spark学习七:spark streaming与flume集成

Spark学习七:spark streaming与flume集成 标签(空格分隔): Spark 一,启动flume flume-conf.properties文件 agent002.sources = sources002 agent002.channels = channels002 agent002.sinks = sinks002 ## define sources agent002.sources.sources002.type = exec agent002.sources.sour

CorePlot学习七---坐标轴的详细分析

先看代码,有标注,很详细,看看是如何设定x.y轴的可视范围.移动范围.已经如何确定原点的位置的.还有就是如何固定坐标轴!!! //坐标轴的初始化 -(void)axesInit { // Setup plot space: 设置一屏内可显示的x,y量度范围 CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)[xyGraph defaultPlotSpace]; plotSpace.delegate = self; plotSpace.allowsUser

cesium 学习(七) HeadingPitchRoll

cesium 学习(七) HeadingPitchRoll 一.前言 对于模型旋转变换.相机视角变换以及方向变换的处理操作,也就是改变heading.pitch.roll这三个的值.这三个值代表什么意思呢?接下来就专门介绍一下Cesium的HeadingPitchRoll. 二.HeadingPitchRoll HeadingPitchRoll其实也就是欧拉(Euler)角:   机体坐标系与地面坐标系的关系是三个Euler角:yaw,pitch,roll,反应了飞机相对地面的姿态.也就是说Ya

tcl脚本学习七:if的学习

lesson 7 : if的学习 1. set x 1; if {$x == 2} {puts "$x is 2"} else {puts "$x is not 2"} if {$x != 1} { puts "$x is != 1" } else { puts "$x is 1" }//if的基本用法 2. if $x==1 {puts "GOT 1"}//可以if里面的判断语句可以不加括号 3. set

Android 学习笔记之AndBase框架学习(七) SlidingMenu滑动菜单的实现

PS:努力的往前飞..再累也无所谓.. 学习内容: 1.使用SlidingMenu实现滑动菜单..   SlidingMenu滑动菜单..滑动菜单在绝大多数app中也是存在的..非常的实用..Github有位牛人将这个东西书写成了一个简单的框架..我们只需要通过引用相关的资源就能够完成一个滑动菜单的实现..有了这一层的基础..那么滑动菜单就很容易实现了..就拿我们最为熟悉的QQ来说吧..当我们进行滑动操作的时候..会有一个新的View去覆盖原本的View..我们可以通过触发新的视图上的控件来执行

Android学习七:new Date使用

1.例子 学习时间函数,并实现了简单的多个按钮监听同一个事件的方法 2.代码 代码很简单,也很清晰 package com.example.datetime; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Activity; import android.app.AlertDialog; import android.os.Bundle; import android.view.Menu;

Python学习(七) 杂学

无论windos还是Linux只要安装了python,配置好了环境变量,则在命令行输入python这个命令的时候就会进入交互模式.在这个模式下可以进行一些简单的python代码编写.退出可以使用exit()方法. Python程序是可以执行的,例如下面代码,保存在hello.py中,如果想要执行这个文件则可以执行命令python hello.py,那么将会输出Hello World. print 'Hello World' 另外,如果想让这个脚本成为可执行文件,则需要在这个文件头上添加 #!/u

[Java并发包学习七]解密ThreadLocal

概述 相信读者在网上也看了很多关于ThreadLocal的资料,很多博客都这样说:ThreadLocal为解决多线程程序的并发问题提供了一种新的思路:ThreadLocal的目的是为了解决多线程访问资源时的共享问题.如果你也这样认为的,那现在给你10秒钟,清空之前对ThreadLocal的错误的认知! 看看JDK中的源码是怎么写的: This class provides thread-local variables. These variables differ from their norm

android学习七(创建自定义控件)

前面学习的是android的基本控件和布局的使用,但是基本的控件和布局有时候并不能实现复杂的布局.我们来看下各种控件和布局的关系. 可见所有的控件都是直接或者间接的继承自View的,所有的布局都是直接或者间接基础自ViewGroup的.View是Android中一种最基本的UI组件,它可以在屏幕的上绘制一块矩形区域,并能响应这块区域的各种事件,因此,我们使用的各种控件其实就是在View的基础上又添加了各种各自特有的功能.而ViewGroup则是一种特殊的View,它可以包含很多的View和子Vi