C#中如何在多个线程间切换

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#中如何在多个线程间切换相关的知识,希望对你有一定的参考价值。

using System;
using System.Threading;

// Simple threading scenario: Start a static method running
// on a second thread.
public class ThreadExample
// The ThreadProc method is called when the thread starts.
// It loops ten times, writing to the console and yielding
// the rest of its time slice each time, and then ends.
public static void ThreadProc()
for (int i = 0; i < 10; i++)

Console.WriteLine("ThreadProc: 0", i);
// Yield the rest of the time slice.
Thread.Sleep(0);



public static void Main()
Console.WriteLine("Main thread: Start a second thread.");
// The constructor for the Thread class requires a ThreadStart
// delegate that represents the method to be executed on the
// thread. C# simplifies the creation of this delegate.
Thread t = new Thread(new ThreadStart(ThreadProc));

// Start ThreadProc. Note that on a uniprocessor, the new
// thread does not get any processor time until the main thread
// is preempted or yields. Uncomment the Thread.Sleep that
// follows t.Start() to see the difference.
t.Start();//导致操作系统将当前实例的状态更改为 ThreadState.Running
//Thread.Sleep(1000);

for (int i = 0; i < 4; i++)

Console.WriteLine("Main thread: Do some work.0", i);
Thread.Sleep(0);//挂起当前线程以使其他等待线程能够执行


Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.");
t.Join();//阻塞调用其他线程,直到本线程终止为止。
Console.WriteLine("Main thread: ThreadProc.Join has returned. Press Enter to end program.");
Console.ReadLine();

上面是MSDN给的一个例子,运行若干次能得到不同的结果。我发现利用sleep(0)并不是每次都能成功地切到另一线程。那么,1。我们一般是如何确保能切换到自己想要线程呢?2。对于单核的CPU来讲,多线程是不是其实还是在串行运行(轮流执行)?
望大侠不吝赐教。。
那我又要问了,3。多线程是干嘛的,难道多线程之间的切换是不可控的吗,还是多线程不存在切换的问题?4。sleep(0)为什么有时候管用有时候不管用呢?我只是想把问题搞明白,期待您的解答~QQ153435466

多谢“lake_cx”的耐心解答,我仔细的看了你的回答,总结一下就是,当前线程挂起后接下来执行哪个线程是不确定的,但是可以通过检查参数来查询当前是哪个线程。
我认为从理论上来讲上面这一段程序的结果应该是:
Main thread: Start a second thread.
Main thread: Do some work.0
ThreadProc:0
Main thread: Do some work.1
ThreadProc:1
Main thread: Do some work.2
ThreadProc:2
Main thread: Do some work.3
ThreadProc:3
Main thread: Call Join(), to wait until ThreadProc ends.
ThreadProc:4
ThreadProc:5
ThreadProc:6
ThreadProc:7
ThreadProc:8
ThreadProc:9
但实际运行结果基本上没有交替,大多数情况下切换到线程t后要等到ThreadProc()中的循环结束后才会继续执行主线程中的循环。那如何能确保得到理论中的输出呢?也就是我所谓的在主线程和线程t直接准确地切换?

楼主你没意识到什么叫切换线程。
多线程是在CPU速度已经足够快的时候用来模拟多任务时处理的,以提高处理效率。
计算机硬件并不是只有CPU,还有很多其他东西,当然,如果时纯粹的计算,多线程不如单线程效率高,因为光线程切换就要花费很多的时间,线程切换是操作系统完成的,需要保存各寄存器状态,进行很多内核句柄的处理,切换一次线程大约要花费处理器1000多个周期左右(折合1000多次累加操作)。但当处理器需要等待其他硬件时(如磁盘读取、网卡收取数据包),如果CPU只处理该线程,那么就只能等其他硬件完成后才能开始下个任务,那么必然导致效率的损失。而对于Windows操作系统而言,最直观的就是CPU在等待其他硬件处理完成前不会响应用户的操作,即界面卡死。
像楼主看的MSDN的例子,sleep方法本来的含义是本线程休眠一定时间,这种休眠与死循环有本质区别,死循环会占用CPU,而sleep不会占用CPU,操作系统会利用该线程sleep的这段时间去处理其他事情(即其他线程或进程),以充分利用CPU。这就导致sleep(0)会导致当前线程停止运行,进入内核状态,同时由操作系统按优先级启动其他线程。这个过程很快,快到让用户感觉线程都是在同时运行的。
按理说输出的结果应该是到后面交替出现的,但是第一次出现的是哪个应该是不确定的,现在楼主出现的不是这样的么?
切换到自己想要的线程:
一般只有一个方法是多个线程的入口时才需要确定此时是哪个线程,但一般是不需要确认的,因为线程启动的入口方法可以带参数,这个可以区别执行时的特定环境。譬如:
Thread t1 = new Thread(new ParameterizedThreadStart(ThreadProc));
t1.Start(1);
Thread t2 = new Thread(new ParameterizedThreadStart(ThreadProc));
t2.Start(2);
Thread t3 = new Thread(new ParameterizedThreadStart(ThreadProc));
t3.Start(3);
public static void ThreadProc(object state)
虽然t1/t2/t3三个线程都会执行ThreadProc,此时使用state就可以确定是属于哪个线程,它为1时表示当时t1在执行,依次类推。
当然直接用ThreadStart的委托也可以,因为里面默认有个参数,那就是this,例如:
Thread t1 = new MyThread(new ThreadStart(ThreadProc));
t1.Num = 1;
t1.Start(1);
Thread t2 = new MyThread(new ThreadStart(ThreadProc));
t2.Num = 2;
t2.Start(2);
Thread t3 = new MyThread(new ThreadStart(ThreadProc));
t3.Num = 3;
t3.Start(3);
那么在ThreadProc中this.Num为1时是t1在执行,依次类推。
理解了线程的原理后就知道了,线程执行顺序是不可控的,最多也只能用sleep让本线程挂起,至于挂起后哪个线程启动,那是操作系统的事了,你管不着。因此像多线程执行的时候一定要考虑到内存的使用,例如t1.Num、t2.Num、t3.Num分别是三个线程自有的变量,一般写代码的时候只由本线程进行修改(当然你硬要其他线程修改也可以做到),因此就不用考虑同步问题。而另一个全局变量,如果三个线程都会做读写操作,那可能就会导致数据出现问题,由于线程执行顺序的不确定性,也会导致数据出现不确定性。
正因为这样,在子线程做完操作后,要将数据展现到界面的时候,需要同步到主线程,然后由主线程将数据展现,因为也许当时主线程也修改了数据,也要展现,那么就出现了不确定性了。当然你可能说我主线程没修改数据,也不展现那个数据,但是编译器并不能判断这个。
参考技术A 多线程在多核CPU中 的作用就不说了,当然是能提升程序的性能
而在单核CPU中也很有用,因为有些程序需要等,比如读硬盘,这种速度非常慢的事情,要单独一个线程去做,这样 程序不用停下来等它..
如果你想切换一个某一个线程到指定的变量上,理论上也是可以做到的。
因为线程的栈是独立的,详细的说,也就是局部变量是独立的,如果你在线程函数运行里搞一个
int threadno, 当运行时 把这个编号传进来..... 当运行到某处时 再比较 如果不同 就SLEEP
参考技术B 如果你想确保能切换到自己想要线程,那跟单线程有什么区别?
第二个问题的答案是肯定的

java-等待唤醒机制(线程中的通信)-线程池

 

 为什么需要线程间的通信

多个线程并发执行时,在默认情况下CPU时随机切换线程的,当我们需要多个线程共同完成一件任务,并且

希望他们有规律的执行,那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

如何保证线程间通信有效利用资源

多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个遍历的使用或操作

就是多个线程在操作同一份书时,避免对同一个共享变量的争夺,也就是我们需要通过一定的手段使各个线程能有效的利用

资源,这个手段就是等待唤醒机制

 

 

 

机制中用到的方法:

 

 注意:

 

 

 

 

 

调用wait和notify方法需要注意的细节:

 

 

 

 

案例分析:生产者和消费者的问题

 

 

包子类

public class BaoZi {
    public String pi;
    public String xian;
    public   boolean flag =false;
}

 

包子铺线程:‘

public class BaoZiPu extends Thread{
    private BaoZi bz;

    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        int count =0;

        while (true){
            synchronized (bz){
                if (bz.flag == true){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(count%2 ==0){
                    bz.pi="pi000";
                    bz.xian="xian0000";
                }else {
                    bz.pi="pi111";
                    bz.xian="xian111";
                }
                count++;
                System.out.println("正在做"+bz.xian+bz.pi);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //做好包子,修改状态
                bz.flag = true;
                //叫醒吃饱人
                bz.notify();
            }
        }

    }
}

 

吃包子线程

public class ChiBaoZI extends Thread{
    private BaoZi bz;

    public ChiBaoZI(BaoZi bz){
        this.bz=bz;
    }

    @Override
    public void run() {
        while (true){
            synchronized (bz){
                if (bz.flag == false){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //唤醒之后
                System.out.println("正在吃包子"+bz.pi+bz.xian);
                //修改包子状态:
                bz.flag = false;
                System.out.println("吃完了");
                System.out.println("+++++++++++++++++");
                bz.notify();//唤醒包子铺做包子

            }
        }
    }



}

 

包子测试:

public class BaoZiTest {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        new BaoZiPu(bz).start();
        new ChiBaoZI(bz).start();
    }
}

 

结果

正在做xian0000pi000
正在吃包子pi000xian0000
吃完了
+++++++++++++++++
正在做xian111pi111
正在吃包子pi111xian111
吃完了
+++++++++++++++++
正在做xian0000pi000
正在吃包子pi000xian0000
吃完了
+++++++++++++++++
正在做xian111pi111
正在吃包子pi111xian111
吃完了
+++++++++++++++++
正在做xian0000pi000
正在吃包子pi000xian0000
吃完了

 

 

线程池

 

 

底层原理

 

 

 

 

 

 

合理利用线程的好处

减低资源消耗,减少创建和销毁线程的次数,每个工作线程都可以被重复里哟ing,可执行多个任务

提高响应速度,当任务到达,可以不要等待线程的创建就能执行

提高线程的客观理性,可以根据系统的可承受能力,调整线程池中工作线程的数目,防止因为消耗过渡的内存,从而把服务器累吧

注意,每个线程需要大约1MB的内存,线程越多,消耗内存也越多

 

 

java中的线程池:

 

 

 

 

建立Runnable 接口的实现类

public class RunnableImpl implements Runnable{

    Lock l = new ReentrantLock();
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());

    }
}

 

建立线程池并使用:

public class DemoThreadPool {
    public static void main(String[] args) {
        ExecutorService es =Executors.newFixedThreadPool(2);
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        //线程池会一直开启,使用完线程,会自动回收到线程池,线程可以继续被使用
        //一般不会销毁线程池
    }
}

 

以上是关于C#中如何在多个线程间切换的主要内容,如果未能解决你的问题,请参考以下文章

异常与多线程

等待与唤醒机制(线程之间的通信)

java-等待唤醒机制(线程中的通信)-线程池

c# 如何切换窗口

While 循环计时如何在带有多个线程的 C# 中工作?

C# Winform多个窗体界面间的切换