2016 学习笔记(二)

来源:互联网 发布:航空大亨4无法连接网络 编辑:程序博客网 时间:2024/05/18 01:08

进程的学习(bigbang)

1. OnExited 是引发的 API 方法 Exited 事件。调用 OnExited 导致 Exited 事件的发生是唯一的方法引发事件的使用和 Process 组件。 OnExited 主要在该组件从派生类时使用。

using System;

using System.Diagnostics;

 

class MyProcess : Process

{

    public void Stop()

    {

        this.CloseMainWindow();

        this.Close();

        OnExited();

    }

}

class StartNotePad

{

    public static void Main(string[] args)

    {

        MyProcess p = new MyProcess();

        p.StartInfo.FileName = "notepad.exe";

        p.EnableRaisingEvents = true;

        p.Exited += new EventHandler(myProcess_HasExited);

        p.Start();

        p.WaitForInputIdle();

        p.Stop();

    }

    private static void myProcess_HasExited(object sender, System.EventArgs e)

    {

        Console.WriteLine("Process has exited.");

    }

}

2. 软件监控外部程序运行状态, 需要外挂一个程序,用于监控另一个程序运行状态,一旦检测到另一程序关闭,就触发一个事件做其他处理。

using System.Diagnostics;//引入Process 类

private Process[] MyProcesses;

MyProcesses = Process.GetProcessesByName("SajetManager");

//需要监控的程序名,该方法带出该程序所有用到的进程  

    foreach (Process myprocess in MyProcesses)  

    {  

        textBox1.Text +=(是一个注册事件的符号) myprocess.ProcessName + "\r\n";  

        if (myprocess.ProcessName.ToLower() == "sajetmanager")  

        {  

            MessageBox.Show("SajetManager");  

            myprocess.EnableRaisingEvents = true;

//设置进程终止时触发的时间  

            myprocess.Exited += new EventHandler(myprocess_Exited);

//发现外部程序关闭即触发方法myprocess_Exited  

        }  

}      

private void myprocess_Exited(object sender, EventArgs e)

//被触发的程序  

    {  

        MessageBox.Show("SajetManager close");  

}  

3. 利用 命名管道进行进程间通讯

NamedPipeServerStream pipeServer = new  NamedPipeServerStream("testpipe", PipeDirection.InOut,  PipeTransmissionMode.Message, PipeOptions.Asynchronous);
 void Form1_Load(object sender, EventArgs e)
   {
           ThreadPool.QueueUserWorkItem(delegate
                {
                   pipeServer.BeginWaitForConnection((o) =>;
                    {
                        NamedPipeServerStream server = (NamedPipeServerStream)o.AsyncState;
                        server.EndWaitForConnection(o);
                        StreamReader sr = new StreamReader(server);
                        StreamWriter sw = new StreamWriter(server);
                        string result = null;
                        string clientName = server.GetImpersonationUserName();
                        while (true)
                        {
                            result = sr.ReadLine();
                            if (result ==null || result =="bye")
                                break;
                            this.Invoke((MethodInvoker)delegate { lsbMsg.Items.Add(clientName+" : "+result); });
                        }
                    }, pipeServer);
                });
         }

4. 查找进程、启用进程、关闭进程

在启动某个程序之前,如果需要先检查改程序是否已经运行,可以查找进程里有没有这个进程,再根据查找进程后的结果进行相应的判断操作。

using ……
using ……
using System.Diagnostics; 

//启用进程
void process()
{
 Process p;//实例化一个Process对象
 p=Process.Start(@"E:\1.txt");//要开启的进程(或 要启用的程序),括号内为绝对路径
 p.Kill();//结束进程

//查找进程、结束进程
void killProcess()
{
     Process[] pro = Process.GetProcesses();

//获取已开启的所有进程

            //遍历所有查找到的进程

            for (int i = 0; i < pro.Length; i++)
            {

                //判断此进程是否是要查找的进程
                if (pro[i].ProcessName.ToString().ToLower() == "pc_task")
                {
                    pro[i].Kill();//结束进程
                }
            }
}

5. 多线程简单例子讲解

.NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多线程,必须先声明引用此名字空间(using System.Threading;)。


a.启动线程
顾名思义,“启动线程”就是新建并启动一个线程的意思,如下代码可实现:
Thread thread1 = new Thread(new ThreadStart( Count));
其中的 Count 是将要被新线程执行的函数。
b.杀死线程
“杀死线程”就是将一线程斩草除根,为了不白费力气,在杀死一个线程前最好先判断它是否还活着(通过 IsAlive 属性),然后就可以调用 Abort 方法来杀死此线程。
c.暂停线程
它的意思就是让一个正在运行的线程休眠一段时间。如 thread.Sleep(1000); 就是让线程休眠1秒钟。
d.优先级
这个用不着解释了。Thread类中hreadPRiority属性,它用来设置优先级,但不能保证操作系统会接受该优先级。一个线程的优先级可分为5种:Normal, AboveNormal, BelowNormal, Highest, Lowest。具体实现例子如下:
thread.Priority = ThreadPriority.Highest;
e.挂起线程
Thread类的Suspend方法用来挂起线程,直到调用Resume,此线程才可以继续执行。如果线程已经挂起,那就不会起作用。
if (thread.ThreadState = ThreadState.Running) 
{
thread.Suspend();
}
f.恢复线程
用来恢复已经挂起的线程,以让它继续执行,如果线程没挂起,也不会起作用。
if (thread.ThreadState = ThreadState.Suspended) 
{
thread.Resume();
}


下面将列出一个例子,以说明简单的线程处理功能。此例子来自于帮助文档。
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. On a uniprocessor, the thread does not get 
// any processor time until the main thread yields. Uncomment 
// the Thread.Sleep that follows t.Start() to see the difference.
t.Start();
//Thread.Sleep(0);

for (int i = 0; i < 4; i++) {
Console.WriteLine("Main thread: Do some work.");
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();
}
}

此代码产生的输出类似如下内容:
Main thread: Start a second thread.
Main thread: Do some work.
ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Call Join(), to wait until ThreadProc ends.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Main thread: ThreadProc.Join has returned. Press Enter to end program.


     在Visul C#中System.Threading 命名空间提供一些使得可以进行多线程编程的类和接口,其中线程的创建有以下三种方法:Thread、ThreadPool、Timer。下面就它们的使用方法逐个作一简单介绍。

一、Thread
      这也许 是最复杂的方法,但它提供了对线程的各种灵活控制。首先你必须使用它的构造函数创建一个线程实例,它的参数比较简单,只有一个ThreadStart 委托:public Thread(ThreadStart start);然后调用Start()启动它,当然你可以利用它的Priority属性来设置或获得它的运行优先级(enum ThreadPriority: Normal、 Lowest、 Highest、 BelowNormal、 AboveNormal)。
      下例首先生成了两个线程实例t1和t2,然后分别设置它们的优先级,接着启动两线程(两线程基本一样,只不过它们输出不一样,t1为“1”,t2为“2”,根据它们各自输出字符个数比可大致看出它们占用CPU时间之比,这也反映出了它们各自的优先级)。
static void Main(string[] args)
{
   Thread t1 = new Thread(new ThreadStart(Thread1));
   Thread t2 = new Thread(new ThreadStart(Thread2));

   t1.Priority = ThreadPriority.BelowNormal ;
   t2.Priority = ThreadPriority.Lowest ;
           t1.Start();
      t2.Start();
   }
public static void Thread1()

   for (int i = 1; i < 1000; i++) 
   {//每运行一个循环就写一个“1”
     dosth();
    Console.Write("1");
   }
   }
public static void Thread2()

   for (int i = 0; i < 1000; i++) 
   {//每运行一个循环就写一个“2”
    dosth();
    Console.Write("2");
   }
}
public static void dosth()
{//用来模拟复杂运算
   for (int j = 0; j < 10000000; j++) 
   {    
    int a=15;
    a = a*a*a*a;
   }
}
以上程序运行结果为:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112

从以上结果我们可以看出,t1线程所占用CPU的时间远比t2的多,这是因为t1的优先级比t2的高,若我们把t1和t2的优先级都设为Normal,结果见下图: 
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
        从上例我们可看出,它的构造类似于win32的工作线程,但更加简单,只需把线程要调用的函数作为委托,然后把委托作为参数构造线程实例即可。当调用Start()启动后,便会调用相应的函数,从那函数第一行开始执行。
      接下来 我们结合线程的ThreadState属性来了解线程的控制。ThreadState是一个枚举类型,它反映的是线程所处的状态。当一个Thread实例 刚创建时,它的ThreadState是Unstarted;当此线程被调用Start()启动之后,它的ThreadState是 Running;  在此线程启动之后,如果想让它暂停(阻塞),可以调用Thread.Sleep() 方法,它有两个重载方法(Sleep(int )、Sleep(Timespan )),只不过是表示时间量的格式不同而已,当在某线程内调用此函数时,它表示此线程将阻塞一段时间(时间是由传递给 Sleep 的毫秒数或Timespan决定的,但若参数为0则表示挂起此线程以使其它线程能够执行,指定 Infinite 以无限期阻塞线程),此时它的ThreadState将变为WaitSleepJoin,另外值得注意一点的是Sleep()函数被定义为了 static?! 这也意味着它不能和某个线程实例结合起来用,也即不存在类似于t1.Sleep(10)的调用!正是如此,Sleep()函数只能由需“Sleep”的线 程自己调用,不允许其它线程调用,正如when to Sleep是个人私事不能由它人决定。但是当某线程处于WaitSleepJoin状态而又不得不唤醒它时,可使用Thread.Interrupt 方法 ,它将在线程上引发ThreadInterruptedException,下面我们先看一个例子(注意Sleep的调用方法):
static void Main(string[] args)

   Thread t1 = new Thread(new ThreadStart(Thread1));
    t1.Start();
      t1.Interrupt ();
     E.WaitOne ();
     t1.Interrupt ();
          t1.Join();
          Console.WriteLine(“t1 is end”);
}
static AutoResetEvent E = new AutoResetEvent(false);
public static void Thread1()
{   
   try
   {//从参数可看出将导致休眠
    Thread.Sleep(Timeout.Infinite); 
   }
   catch(System.Threading.ThreadInterruptedException e)
   {//中断处理程序
    Console.WriteLine (" 1st interrupt");
   }
     E.Set ();
   try
   {// 休眠
    Thread.Sleep(Timeout.Infinite ); 
   }
   catch(System.Threading.ThreadInterruptedException e)
   {
     Console.WriteLine (" 2nd interrupt");
   }//暂停10秒
           Thread.Sleep (10000); 
        }
运行结果为:1st interrupt
                  2nd interrupt
                  (10s后)t1 is end


从上例我们可以看出Thread.Interrupt方法可以把程序从某个阻塞(WaitSleepJoin)状态唤醒进入对应的中断处理程序,然后继续往下执行(它的ThreadState也变为Running),此函数的使用必须注意以下几点:
      1、此方法不仅可唤醒由Sleep导致的阻塞,而且对一切可导致线程进入WaitSleepJoin状态的方法(如Wait和Join)都有效。如上例所示, 使用时要把导致线程阻塞的方法放入try块内, 并把相应的中断处理程序放入catch块内。
      2、对 某一线程调用Interrupt, 如它正处于WaitSleepJoin状态, 则进入相应的中断处理程序执行, 若此时它不处于WaitSleepJoin状态, 则它后来进入此状态时, 将被立即中断。若在中断前调用几次Interrupt, 只有第一次调用有效, 这正是上例我用同步的原因, 这样才能确保第二次调用Interrupt在第一个中断后调用,否则的话可能导致第二次调用无效(若它在第一个中断前调用)。你可以把同步去掉试试,其结 果很可能是:   1st interrupt
      上例还 用了另外两个使线程进入WaitSleepJoin状态的方法:利用同步对象和Thread.Join方法。Join方法的使用比较简单,它表示在调用此 方法的当前线程阻塞直至另一线程(此例中是t1)终止或者经过了指定的时间为止(若它还带了时间量参数),当两个条件(若有)任一出现,它立即结束 WaitSleepJoin状态进入Running状态(可根据.Join方法的返回值判断为何种条件,为true,则是线程终止;false则是时间 到)。线程的暂停还可用Thread.Suspend方法,当某线程处于Running状态时对它调用Suspend方法,它将进入 SuspendRequested状态,但它并不会被立即挂起,直到线程到达安全点之后它才可以将该线程挂起,此时它将进入Suspended状态。如对 一个已处于Suspended的线程调用则无效,要恢复运行只需调用Thread.Resume即可。
      最后我 们谈的是线程的销毁,我们可以对需销毁的线程调用Abort方法,它会在此线程上引发ThreadAbortException。我们可把线程内的一些代 码放入try块内,并把相应处理代码放入相应的catch块内,当线程正执行try块内代码时如被调用Abort,它便会跳入相应的catch块内执行, 执行完catch快内的代码后它将终止(若catch块内执行了ResetAbort则不同了:它将取消当前Abort请求,继续向下执行。所以如要确保 某线程终止的最好用Join,如上例)。

二、ThreadPool
线程池 (ThreadPool)是一种相对较简单的方法,它适应于一些需要多个线程而又较短任务(如一些常处于阻塞状态的线程) ,它的缺点是对创建的线程不能加以控制,也不能设置其优先级。由于每个进程只有一个线程池,当然每个应用程序域也只有一个线程池(对线),所以你将发现 ThreadPool类的成员函数都为static! 当你首次调用ThreadPool.QueueUserWorkItem、 ThreadPool.RegisterWaitForSingleObject等,便会创建线程池实例。下面就线程池当中的两函数作一介绍:
public static bool QueueUserWorkItem( //调用成功则返回true
WaitCallback callBack,//要创建的线程调用的委托
        object state //传递给委托的参数
)//它的另一个重载函数类似,只是委托不带参数而已
此函数的作用是把要创建的线程排队到线程池,当线程池的可用线程数不为零时(线程池有创建线程数的限制,缺身值为25),便创建此线程,否则就排队到线程池等到它有可用的线程时才创建。
public static RegisteredWaitHandle RegisterWaitForSingleObject(
   WaitHandle waitObject,// 要注册的 WaitHandle
   WaitOrTimerCallback callBack,// 线程调用的委托
   object state,//传递给委托的参数
   int TimeOut,//超时,单位为毫秒,
   bool executeOnlyOnce file://是否只执行一次
); 
public delegate void WaitOrTimerCallback(
   object state,//也即传递给委托的参数
   bool timedOut//true表示由于超时调用,反之则因为waitObject
);
      此函数 的作用是创建一个等待线程,一旦调用此函数便创建此线程,在参数waitObject变为终止状态或所设定的时间TimeOut到了之前,它都处于“阻 塞”状态,值得注意的一点是此“阻塞”与Thread的WaitSleepJoin状态有很大的不同:当某Thread处于WaitSleepJoin状 态时CPU会定期的唤醒它以轮询更新状态信息,然后再次进入WaitSleepJoin状态,线程的切换可是很费资源的;而用此函数创建的线程则不同,在 触发它运行之前,CPU不会切换到此线程,它既不占用CPU的时间又不浪费线程切换时间,但CPU又如何知道何时运行它?实际上线程池会生成一些辅助线程 用来监视这些触发条件,一旦达到条件便启动相应的线程,当然这些辅助线程本身也占用时间,但是如果你需创建较多的等待线程时,使用线程池的优势就越加明 显。见下例:
static AutoResetEvent ev=new AutoResetEvent(false);
public static int Main(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
      ev,
      new WaitOrTimerCallback(WaitThreadFunc),
      4,
      2000,
      false//表示每次完成等待操作后都重置计时器,直到注销等待
      );
ThreadPool.QueueUserWorkItem (new WaitCallback (ThreadFunc),8);
Thread.Sleep (10000);
   return 0;
}
    public static void ThreadFunc(object b)
{ Console.WriteLine ("the object is {0}",b);
for(int i=0;i<2;i++)
{ Thread.Sleep (1000);
   ev.Set();
}
}
public static void WaitThreadFunc(object b,bool t)
{ Console.WriteLine ("the object is {0},t is {1}",b,t);
    }
其运行结果为: 
the object is 8
the object is 4,t is False
the object is 4,t is False
the object is 4,t is True
the object is 4,t is True
the object is 4,t is True
      从以上 结果我们可以看出线程ThreadFunc运行了1次,而WaitThreadFunc运行了5次。我们可以从WaitOrTimerCallback中 的bool t参数判断启动此线程的原因:t为false,则表示由于waitObject,否则则是由于超时。另外我们也可以通过object b向线程传递一些参数。

3、Timer
      它适用于需周期性调用的方法,它不在创建计时器的线程中运行,它在由系统自动分配的单独线程中运行。这和Win32中的SetTimer方法类似。它的构造为:
public Timer(
   TimerCallback callback,//所需调用的方法
   object state,//传递给callback的参数
   int dueTime,//多久后开始调用callback
   int period//调用此方法的时间间隔
); // 如果 dueTime 为0,则 callback 立即执行它的首次调用。如果 dueTime 为 Infinite,则 callback 不调用它的方法。计时器被禁用,但使用 Change 方法可以重新启用它。如果 period 为0或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。如果 period 为零 (0) 或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。
      在创建计时器之后若想改变它的period和dueTime,我们可以通过调用Timer的Change方法来改变:
public bool Change(
   int dueTime,
   int period
);//显然所改变的两个参数对应于Timer中的两参数
public static int   Main(string[] args)

Console.WriteLine ("period is 1000");
Timer tm=new Timer (new TimerCallback (TimerCall),3,1000,1000);
Thread.Sleep (2000);
Console.WriteLine ("period is 500");
tm.Change (0,800);
Thread.Sleep (3000);
return 0;
   }
public static void TimerCall(object b)

Console.WriteLine ("timercallback; b is {0}",b);
}
其运行结果为:
period is 1000
timercallback;b is 3 
timercallback;b is 3 
period is 500
timercallback;b is 3 
timercallback;b is 3 
timercallback;b is 3 
timercallback;b is 3 
                        
总结
      从以上 的简单介绍,我们可以看出它们各自使用的场合:Thread适用于那些需对线程进行复杂控制的场合;ThreadPool适应于一些需要多个线程而又较短 任务(如一些常处于阻塞状态的线程);Timer则适用于那些需周期性调用的方法。只要我们了解了它们的使用特点,我们就可以很好的选择合适的方法。

6.信号量

信号量说简单点就是为了线程同步,或者说是为了限制线程能运行的数量。

那它又是怎么限制线程的数量的哩?是因为它内部有个计数器,比如你想限制最多5个线程运行,那么这个计数器的值就会被设置成5,如果一个线程调用了 这个Semaphore,那么它的计数器就会相应的减1,直到这个计数器变为0。这时,如果有另一个线程继续调用这个Semaphore,那么这个线程就 会被阻塞。

获得Semaphore的线程处理完它的逻辑之后,你就可以调用它的Release()函数将它的计数器重新加1,这样其它被阻塞的线程就可以得到调用了。

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

namespace Semaphore1
{
    class Program
    {
        //我设置一个最大允许5个线程允许的信号量
        //并将它的计数器的初始值设为0
        //这就是说除了调用该信号量的线程都将被阻塞
        static Semaphore semaphore =new Semaphore(0,5);

        static void Main(string[] args)
        {
            for (int i =1; i <=5; i++)
            {
                Thread thread = new Thread(new ParameterizedThreadStart(work));

                thread.Start(i);
            }

            Thread.Sleep(1000);
            Console.WriteLine("Main thread over!");

            //释放信号量,将初始值设回5,你可以将
            //将这个函数看成你给它传的是多少值,计数器
            //就会加多少回去,Release()相当于是Release(1)
            semaphore.Release(5);
        }

        static void work(object obj)
        {
            semaphore.WaitOne();

            Console.WriteLine("Thread {0} start!",obj);
            
            semaphore.Release();
        }
    }
}

其它的线程只有等到主线程释放才会执行,因为我给信号量计数器的初始值是0,所以其它线程在主线程释放前都会被阻塞。而后,我在主线程直接用Release()函数将计数器置为5,所以5个线程可以同时得到执行。

另外,可以给信号量设置一个名称,这个名称是操作系统可见的,因此,可以使用这些信号量来协调跨进程边界的资源使用。

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

namespace Semaphore2
{
    class Program
    {
        static void Main(string[] args)
        {
            Semaphore seamphore = new Semaphore(5,5,"SemaphoreExample");

            seamphore.WaitOne();
            Console.WriteLine("Seamphore 1");
            seamphore.WaitOne();
            Console.WriteLine("Seamphore 2");
            seamphore.WaitOne();
            Console.WriteLine("Seamphore 3");

            Console.ReadLine();
            seamphore.Release(3);
        }
    }
}

 

7. 等待信号和触发信号 - Signaling with Wait and Puls

前面讨论事件等待句柄--一个简单的信号机制,一个线程一直阻塞直到接收到另外一个线程的通知。

一个更强大的信号机制被Monitor类所经由静态函数Wait和Pluse(及PulseAll)提供。你自己编写通知逻辑,使用自定义标记和字 段(加上锁),然后引入Wait和Pluse命令来阻止自旋。就lock语句和这些函数,你就可以完成 AutoResetEvent,ManualResetEven和Semaplore的功能,同样你也可以完成WaitHandle静态函数 WaitAll和WaitAny的功能。因此,Wait和Pluse能够在等待句柄使用的地方可以使用。

Wait和Pluse信号比起等待事件句柄也有不足的地方:

· Wait/Pluse不能跨越应用程序域或应用程序。

· 你比较保护与信号逻辑相关的所有变量

· Wait/Pluse编程可能会依赖Microsoft文档的开发者混淆。

在文档中没有很明显地告诉你如何使用Wait和Pluse,即使你读完了它们是如何工作。Wait和Pluse对于业余的人来说特别讨厌。幸运的是,这里有一个很简单的模式来驯服Wait和Pluse。

Pluse大概耗费100ns,是调用Set函数的三分之一。在不竞争信号上等等的时间完全由你决定--因为你自己实现了逻辑。

How to Use Wait and Pluse

1. 定义一个熟悉用于同步对象,如:readonly object _locker = new object();

2. 定义一些字段用于自定义阻塞条件,如: bool _go; or int _semaphoreCount;

3. 在你想阻塞时使用以下代码:lock(_locker) while(<blocking-condition>) Monitor.Wait(_locker);

4. 使用下面的代码来改变阻塞条件:lock(_locker){ < alter the field(s) or data that might impact the blocking condition(s) >; Monitor.Pluse(_locker);/*or: Monitor.PluseAll(_locker);*/}

这种方式允许你让任何线程为任何条件等待任何时间。下面是一个简单的例子,工作线程等待直到_go字段为true:

class SimpleWaitPluse

{

    static object _locker =new object();

    static bool _go;

 

    static void Main()

    {

        new Thread(Work).Start();

    

        Console.ReadLine();

 

        lock(_locker)

        {

            _go=true;

            Monitor.Pluse(_locker);

        }

    }

    static void Work()

    {

        lock(_locker)

        {

            while(!go)Monitor.Wait(_locker);

        }

        Console.WriteLine("Woken!");

  }

}

为了线程安全,必须确保所有共享字段都加锁。因此,围绕着读/更新_go标记添加一行lock语句。这很关键(除非你想使用非阻塞同步原理)。

Work函数一直阻塞,直到_go为true。Monitor.Wait做了以下的事情,按顺序:

1. _locker对象上释放锁。

2. 阻塞直到_lcoker被触发。

3. _locker对象上重新获取锁。如果所被竞争,那么它一直阻塞直到所可以使用。

Monitor.Wait要求在lock语句内使用,否则将仍处一个异常。对于Monitor.Pluse也一样。

Main中,通过设置_go为true且调用Pluse来通知工作线程。只要一释放锁,工作线程立马执行。

Pluse和PluseAll释放阻塞在Wait上的线程。Pluse最多释放一个线程;Pluse释放所有。这个例子中只有一个线程阻塞。如果多个线程使用我们的建议调用PluseAll更加安全。

为了让Wait和Pluse或PluseAll通讯,同步对象必须是相同的(这个例子是_locker)。

在我们的模式中,触发(Pluse)表示有些东西已经改变,等待线程必须重新检查阻塞条件。Work工作线程中通过while循环来检查。然后,等待者决定是否继续。

移除while循环就得到一个皮包骨头的例子:

class SimpleWaitPluse

{

    static object _locker =new object();

    static bool _go;

 

    static void Main()

    {

        new Thread(Work).Start();

    

        Console.ReadLine();

 

        lock(_locker)

        {

            _go=true;

            Monitor.Pluse(_locker);

        }

    }

    static void Work()

    {

        lock(_locker)

        {

            Monitor.Wait(_locker);

        }

        Console.WriteLine("Woken!");

  }

}

它的输出是不确定的。如果Wait先执行,那么工作正常。如果Pluse线执行,那么这个通知将丢失,工作线程永远卡住。这一点与AutoResetEvent是不同的,Set语句将有一块内存并锁住影响,所以即使在WaitOne之前调用它也是有效的。

Pluse不会锁住自己,所以必须用go标记。这就是Wait和Pluse的才艺:我们使用一个bool标记,可以使他像 AutoResetEvent一样工作;使用一个整数字段,可以编写一个CountdownEvent Seamphore。使用更多的数据结构,可以编写一个生产/消耗者队列。

生产/消耗者队列

前面已经描述了生产/消耗者队列的概念,及如何使用AutoResetEvent来编写。现在使用Wait和Pluse来编写更强大的生产/消耗者队列。这次允许任意数量的工作项,每一个拥有自己的线程。使用数组来跟踪线程,这使得可以在关闭对列时使用Join选项。

每个工作线程执行一个Consume函数。创建和启动这些线程在一个循环中。将使用更灵活的方法而不是字符串来描述一个任务。使用 System.Action委托,它可以匹配任何参数方法,不像ThreadStart委托。仍然调用带有参数的方法来表示任务,通过封装在一个匿名函数 或lambda表达式中。使用Queue<T>集合来表示任务的队列。

下面是完整的代码:

using System;

using System.Threading;

using System.Collections.Generic;

public class PCQueue

{

  readonly object _locker =new object();

  Thread[] _workers;

  Queue<Action> _itemQ = new Queue<Action>();

  public PCQueue (int workerCount)

  {

    _workers = new Thread [workerCount];

    // Create and start a separate thread for each worker

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

      (_workers [i] = new Thread (Consume)).Start();

  }

  public void Shutdown (bool waitForWorkers)

  {

    // Enqueue one null item per worker to make each exit.

    foreach (Thread worker in _workers)

      EnqueueItem (null);

    // Wait for workers to finish

  if (waitForWorkers)

      foreach (Thread worker in _workers)

        worker.Join();

  }

  public void EnqueueItem (Action item)

  {

    lock (_locker)

    {

      _itemQ.Enqueue (item);           // We must pulse because we‘re

Monitor.Pulse (_locker);         // changing a blocking condition.

    }

  }

  void Consume()

  {

    while (true)                        // Keep consuming until

    {                                   // told otherwise.

      Action item;

      lock (_locker)

      {

        while (_itemQ.Count == 0) Monitor.Wait (_locker);

        item = _itemQ.Dequeue();

      }

      if (item == null) return;         // This signals our exit.

      item();                           // Execute item.

    }

  }

}

8. 详解c#中AutoResetEvent

相关的概念

1.线程同步
ManualResetEvent
Set()方法将状态设置为有信号
Reset()将其设置为无信号
WaitOne()将阻塞到其有信号为止,若调用WaitOne的时刻就是有信号的,将不会阻塞

AutoResetEvent
ManualResetEvent的区别是,AutoResetEvent.WaitOne()会自动改变事件对象的状态,即AutoResetEvent.WaitOne()每执行一次,事件的状态就改变一次。有信号-->无信号;无信号-->有信号

Code
  class MyMainClass
    {
       const int numIterations =10;       
       // 初始的时候是没有信号的,这里的意思是指参数false
       static AutoResetEvent myResetEvent = newAutoResetEvent(false);
       static int number;

static void Main()
       {
           Thread myReaderThread = new Thread(newThreadStart(MyReadThreadProc));
           myReaderThread.Name = "ReaderThread";
           myReaderThread.Start();

for (int i = 1; i <= numIterations; i++)
           {
               Console.WriteLine("Writer thread writing value: {0}", i);
               number = i;

// 发信号,说明值已经被写进去了。这里的意思是说Set是一个发信号的方法。
               myResetEvent.Set();

Thread.Sleep(10000);
           }

myReaderThread.Abort();
       }

static void MyReadThreadProc()
       {
           while (true)
           {
               //在数据被作者写入之前不会被读者读取
               myResetEvent.WaitOne();
               Console.WriteLine("{0} reading value: {1}",Thread.CurrentThread.Name, number);
           }
       }
    }

myResetEven.Set(),其实就相当于一个开关,如果没有执行set()方法,下面的waitOne()就等不到让它执行的通知信号,这样一来waitOne后面的语句也不会执行了.

Monitor方法
Wait()Pulse()机制用于线程间交互作用,当一个对象发出Monitor.Wait(),正在访问对象的线程将保持等待状态.直到它得到一个唤醒的信号.Monitor.Pulse()用于给等待线程发送信号.
也就是说在Monitor.Enter() Monitor.Exit()之间的临界区域,要释放对象上的锁并阻塞当前线程,自己处于队列中等待状态.


Lock与Mutex区别
Lock   进程间多个线程同步
Mutex 进程间多个线程同步,或多个进程的同步

.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到,他们的用法很类似,但也有区别。

Set方法将信号置为发送状态,Reset方法将信号置为不发送状态,WaitOne等待信号的发送。可以通过构造函数的参数值来决定其初始状态, 若为true则非阻塞状态,为false为阻塞状态。如果某个线程调用WaitOne方法,则当信号处于发送状态时,该线程会得到信号,继续向下执行。

其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号 后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent 一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。

也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。

9进程间通信

protected override void DefWndProc(ref Message m)

{

  if (m.Msg == 0x200)

  {

   MessageBox.Show("捕捉到消息");

  }

  else

  {

  }

  base.DefWndProc(ref m);

}

此方法重写了窗体的消息截获代码,运行后会发现,鼠标一移向窗体就会弹窗

对于一个可视控件来说,是不断的在接受系统发送的消息的。比如鼠标悬停在某某控件上,就是一个消息,移出这个控件又是一个消息。如示例所示,其实鼠标移入窗体,窗体就获得了一个消息,无论你写没写过代码,它都获得了这个消息,一个消息对应触发一个事件,编写了事件代码,就会执行相应的代码操作。

事件里编写的代码,和方法里写的代码,最主要的不同就在于前者是不知道何时触发,而后者是自己调用运行到那里就执行的。

那谁来决定某个事件何时触发呢?那就是消息

示例中的0x200是一个消息类型,代表了鼠标移入窗体这个消息

现在讨论一下如何利用这种消息机制来在进程之间传递值

需求:

程序A的主窗体里有一个全局变量

程序B的主窗体里有一个按钮,点击这个按钮,获取到程序A的这个变量

实现:

1. 新建一个解决方案,是程序A,窗体后台代码重写DefWndProc

public partial class Form1 : Form

{

  public Form1()

  {

   InitializeComponent();

  }

  private void Form1_Load(object sender, EventArgs e)

  {

  }

  protected override void DefWndProc(ref Message m)

  {

   if (m.Msg == 0x104)

   {

    m.Result = (IntPtr)333;

    return;

       }

   else

   {    

   }

   base.DefWndProc(ref m);

  }

}

新建一个解决方案,程序B

public Form1()

{

 InitializeComponent();

}

[DllImport("User32.dll", EntryPoint = "SendMessage")]

private static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

 

private void button2_Click(object sender, EventArgs e)

{

 Process[] arrPro = Process.GetProcessesByName("WindowsFormsApplication1.vshost");

 IntPtr ip = SendMessage(arrPro[0].MainWindowHandle, 0x104, 1, 2);

}

这样,在按钮点击的时候,会向A发送一个消息,消息类型是104,两个参数12A能捕获到,设置结果为333,那最后在B中的ip的值就是333

 

10. 总结C#启动外部程序的几种方法

C# 启动命令行程序

例如:做上网拨号程序可以启动命令行Rasdial 程序。

--------------------------------------------------------------- 

            Process pro = new Process();
            pro.StartInfo.FileName = "cmd";
            pro.StartInfo.Arguments = "/c" + "Rasdial/?";
            pro.StartInfo.UseShellExecute = false;
            pro.StartInfo.RedirectStandardInput = true;
            pro.StartInfo.RedirectStandardOutput = true;
            pro.StartInfo.RedirectStandardError = true;
            pro.StartInfo.CreateNoWindow = true;
            pro.Start();           

            //string.rdText = pro.StandardOutput.ReadToEnd(); //执行的结果内容
            pro.StandardInput.WriteLine("exit"); //要退出,不然执行下一个程序时候会出错

例如:

C#调用CMD.exe,执行DOS命令,编码  FLV

--------------------------------------------------------------- 

Process p = new Process();
   p.StartInfo.FileName = "cmd.exe";
   p.StartInfo.UseShellExecute = false;
   p.StartInfo.RedirectStandardInput = true;
   p.StartInfo.RedirectStandardOutput = true;
   p.StartInfo.RedirectStandardError = true;
   p.StartInfo.CreateNoWindow = true;
   p.Start();
   string strOutput=null;
//   p.StandardInput.WriteLine("cd D:\\flv\\mplayer");
//   p.StandardInput.WriteLine("cd d:");
   p.StandardInput.WriteLine(string.Format("D:\\flv\\mplayer\\mencoder \"c:\\vs.wmv\" -o \"c:\\output.flv\" -of lavf  -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -oac mp3lame -lameopts abr:br=56 -ovc lavc -lavcopts vcodec=flv:vbitrate={0}:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:dia=4:cmp=6:vb_strategy=1 -vf scale=512:-3 -ofps 12 -srate 22050",200));
  
   p.StandardInput.WriteLine("exit");
   strOutput = p.StandardOutput.ReadToEnd();
   Console.WriteLine(strOutput);
   p.WaitForExit();
   p.Close();

记得同时要导入:using System.Diagnostics;命名空间。

 

例如:使用C#调用外部Ping命令获取网络连接情况
--------------------------------------------------------------- 
首先,我们用使用Process类,来创建独立的进程,导入System.Diagnostics,

using System.Diagnostics;

实例一个Process类,启动一个独立进程

Process p = new Process();

Process类有一个StartInfo属性,这个是ProcessStartInfo类,包括了一些属性和方法,

下面我们用到了他的几个属性:

设定程序名

p.StartInfo.FileName = "cmd.exe";

关闭Shell的使用

p.StartInfo.UseShellExecute = false;

重定向标准输入

p.StartInfo.RedirectStandardInput = true;

重定向标准输出

p.StartInfo.RedirectStandardOutput = true;

重定向错误输出

p.StartInfo.RedirectStandardError = true;

设置不显示窗口

p.StartInfo.CreateNoWindow = true;

上面几个属性的设置是比较关键的一步。

既然都设置好了那就启动进程吧,

p.Start();

输入要执行的命令,这里就是ping了,

p.StandardInput.WriteLine("ping -n 1 192.192.132.229");

p.StandardInput.WriteLine("exit");

从输出流获取命令执行结果,

string strPingTXT = p.StandardOutput.ReadToEnd();

通过分析strPingTXT就知道连接情况了。

 

C# 启动应用程序的几种方法:

    1. 启动应用程序,不等待其退出。
    2. 启动应用程序,等待其退出。
    3. 启动应用程序,无限等待其退出。
    4. 启动应用程序,通过事件监视其退出。

    // using System.Diagnostics;
    private string appName = "calc.exe";

    /// <summary>
    /// 1. 启动外部应用程序,不等待其退出
    /// </summary>
    private void button1_Click(object sender, EventArgs e)
    {
        Process.Start(appName);
        MessageBox.Show(String.Format("外部程序 {0} 启动完成!", this.appName), this.Text, 
        MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    /// <summary>
    /// 2. 启动外部应用程序,等待其退出
    /// </summary>
    private void button2_Click(object sender, EventArgs e)
    {
        try
        {
            Process proc = Process.Start(appName);
            if (proc != null)
            {
                proc.WaitForExit(3000);
                if (proc.HasExited)
                    MessageBox.Show(String.Format("外部程序 {0} 已经退出!", this.appName), this.Text,
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                else
                {
                    // 如果外部应用程序没有结束运行则强行终止之。
                    proc.Kill();
                    MessageBox.Show(String.Format("外部程序 {0} 被强行终止!", this.appName), this.Text,
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
        }
        catch (ArgumentException ex)
        {
            MessageBox.Show(ex.Message, this.Text,
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }


    /// <summary>
    /// 3. 启动外部应用程序,无限等待其退出
    /// </summary>
    private void button3_Click(object sender, EventArgs e)
    {
        try
        {
            Process proc = Process.Start(appName);
            if (proc != null)
            {
                proc.WaitForExit();
                MessageBox.Show(String.Format("外部程序 {0} 已经退出!", this.appName), this.Text,
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        catch (ArgumentException ex)
        {
            MessageBox.Show(ex.Message, this.Text,
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }


    /// <summary>
    /// 4. 启动外部应用程序,通过事件监视其退出
    /// </summary>
    private void button4_Click(object sender, EventArgs e)
    {
        try
        {
            // 启动外部应用程序
            Process proc = Process.Start(appName);
            if (proc != null)
            {
                // 监视进程退出
                proc.EnableRaisingEvents = true;
                // 指定退出事件方法
                proc.Exited += new EventHandler(proc_Exited);
            }
        }
        catch (ArgumentException ex)
        {
            MessageBox.Show(ex.Message, this.Text,
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
   
    /// <summary>
    /// 外部程序退出事件
    /// </summary>
    void proc_Exited(object sender, EventArgs e)
    {
        MessageBox.Show(String.Format("外部应用程序程序 {0} 已经退出!", this.appName), this.Text,
            MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

 例如:调用外部程序进行解压和压缩。 
--------------------------------------------------------------- 
 
使用Process对象: 
System.Diagnostics.Process  p=new  System.Diagnostics.Process(); 
p.StartInfo.FileName="arj.exe"  ;//需要启动的程序名 
p.StartInfo.Arguments="-x  sourceFile.Arj  c:\temp";//启动参数 
p.Start();//启动 
if(p.HasExisted)//判断是否运行结束 
 p.kill();

11. c#编写winForm程序,其中需要调用adb shell getprop去得到设备信息,并显示在一textbox内。

string cmd = @"C:\..\adb.exe";
ProcessStartInfo psi = null;
psi = new ProcessStartInfo(cmd, "shell getprop");
Process p = Process.Start(psi);
p.WaitForExit();

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 驾驶证年过期了怎么办 驾照过期六个月怎么办 移动预约号码取消怎么办 身份证换地址驾驶证怎么办 刚来成都怎么办居住证 我在外地怎么办身份证 身份证丢在外地怎么办 换领新身份证时旧证丢了怎么办 二代身份证重号怎么办 北京行驶证到期怎么办 北京驾驶证即将过期怎么办 去澳门没有网络怎么办 三个周期未年检怎么办 深圳驾照丢了怎么办 武汉驾照年审过期怎么办 武汉驾照过期了怎么办 科二过不了怎么办 南京身份证到期换新怎么办 过期身份证丢了怎么办 南京驾照过期了怎么办 换驾照身体证明怎么办 学车办理暂住证怎么办 a牌驾照扣分怎么办 b牌驾照扣分怎么办 b驾照扣分了怎么办 考驾照要暂住证怎么办 换驾驶证有色盲怎么办 外籍人员办理就业证怎么办 驾驶证该审过期怎么办 小车证扣满12分怎么办 b证扣满12分怎么办 车过户后保险怎么办 换新轮胎旧轮胎怎么办 驾照一审超一年怎么办 驾驶证年检过期了怎么办 交管12123怎么办进京证 驾照登录密码忘记怎么办 预约密码忘记了怎么办 学驾照密码忘记怎么办 考驾照不识字怎么办 驾校密码忘了怎么办