多线程,匿名委托,异步调用

常规经典多线程和异步

简单的线程处理 及同步:

public class MuThread
{
    int a = 0;

    int i = 0;
    private volatile bool _run=true;
    public void method(object par)
    {
        while (_run)
        {
            Console.WriteLine("运行中"+par.ToString());
        }
        Console.WriteLine("已结束");
        
    }
    public void stop()
    {
        _run = false;
    }

    public void method1()
    {
        while (a < 100)
        {
            a++; Console.WriteLine("线程1输出:" + a);
        }
    }
    public void method2()
    {
        //当不加入线程同步代码的时候调用method1跟method2 会出现混乱
        //但是通过数他们的输出行数依然是100行 
        //说明while判断是正确的 
        //当CPU分配时间片的时候 无论他们有多么接近 其实同一时间依然只有一个线程在访问变量a

        //但是线程1跟2是CPU随机分配时间片交替前进的 实现同一时间多个进度协同执行任务的效果

        while (a < 100)
        {
            a++; Console.WriteLine("线程2输出:" + a);
        }
    }

    public void methodSyn1()
    {
        while (a < 100)
        {
            lock (this)
            {
                //意思是代码进入lock范围后 大括号内的代码将会被单独占用直到执行完
                //而不会临时被其他时间片插入导致a的值已经更改
                //当时间片继续回到此段时还沿用原来的 变量 导致混乱
                //如果去掉前面的判断 会输出101 ,至于原理么跟孙鑫C++线程同步是一样的 也有Monitor.Entry()
                if (a < 100)
                {
                    a++; Console.WriteLine("线程1输出:" + a);
                }
            }
        }
    }

    public void methodSyn2()
    {
        while (a < 100)
        {
            Monitor.Enter(this);
            if (a < 100)
            {
                a++; Console.WriteLine("线程2输出:" + a);
            }
            Monitor.Exit(this);
        }
    }
}

 

线程示例:

static void Main()
{
    //Service s = new Service();
    //s.start();

    //初始化MuThread的时候可以在构造函数 中传入一个方法引用也就是delegate
    //然后再MuThread实例的线程方法执行完毕后调用该delegate 
    //这种编程方式我一直认为很高深 被那些砖家称之为"回调"callback
    //参见ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_fxadvance/html/52b32222-e185-4f42-91a7-eaca65c0ab6d.htm
    MuThread m = new MuThread();

    //ThreadStart 其实是一个委托
    //在C++里就需要声明委托 
    //为了给方法传参数但是不能为每种类型都定义一个委托 所以只能传入object类型

    //.Net为我们简化了的语法
    Thread tPar = new Thread(m.method);
    tPar.Start("传入的参数");

    //ParameterizedThreadStart委托显示语法 start()其实相当于Event.do()            
    ParameterizedThreadStart del = new ParameterizedThreadStart(m.method);
    Thread tPar2 = new Thread(del);
    tPar2.Start("传入的参数");

    Thread t = new Thread(new ThreadStart(m.method1));
    Thread t2 = new Thread(new ThreadStart(m.method2));
    t2.Start();
    t.Start();
    //利用volatile 参数的状态终止线程
    while (!tPar.IsAlive) ;

    Thread.Sleep(100);
    m.stop();
    //MSDN的意思是阻断当前调用t的线程 也就是Main 主线程 直到让t执行完毕
    //事实确实是这样的 当不加Join()的时候 看着t.start()的语句在main方法下面的循环语句前面
    //其实他们的时间片依然由CPU随机分配的 "主线程调用"这句输出是穿插在前面子线程输出一起的
    //参见ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/fxref_mscorlib/html/56ed7b6f-efe0-67e7-34bc-766dd9f693f9.htm
    //当主线程 也就是Main方法里进行Thread.Sleep(200)时 其实是挂起主线程 这样就可以把时间片让给那些子线程
    t.Join();
    for (int i = 0; i < 50; i++)
    {
        Console.WriteLine("主线程调用");
    }

    //使用线程池
    //ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_csref/html/5426dae4-c3f3-4e76-a998-19f7ca4baf3f.htm
    Console.ReadKey();
}

 又反复测试了下join其实感觉就像endInvoke 就是阻止,说白了把指定的线程join到当前主线程 说白了就是阻塞操作 可不就是endInvoke么  ,到trd.Join()的 地方位置 必须要等待trd执行完毕

使用backgroundworker组件:

 

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.WorkerSupportsCancellation = true;
        button3.Enabled = false;
    }

    private void button2_Click(object sender, EventArgs e)//start
    {
        textBox1.Text = "开始产生10000以内的随机数。。。\n\n";
        button2.Enabled = false;
        button3.Enabled = true;
        //在后台开始操作
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        //不要直接使用组件实例名称(backgroundworker1)因为有多个backgroundworker时
        //直接使用会产生耦合问题 应该通过下面的转换使用它
        BackgroundWorker worker = sender as BackgroundWorker;
        //下面的内容相当于线程要处理的内容。//注意 不要在此事件中和界面控件打交道
        Random r = new Random();
        int numCount = 0;
        while (worker.CancellationPending==false)
        {
            int num = r.Next(10000);
            if (num%5==0)
            {
                numCount++;
                worker.ReportProgress(0, num);
                Thread.Sleep(1000);
            }
        }
        e.Result = numCount;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        int num = (int)e.UserState;
        textBox1.Text += num + " ";
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error==null)
        {
            textBox1.Text += "\n\n操作停止,共产生" + e.Result + "个能被5整除的随机数";
        }
        else
        {
            textBox1.Text += "\n操作过程中产生错误:" + e.Error;
        }
    }

    private void button3_Click(object sender, EventArgs e)
    {
        backgroundWorker1.CancelAsync();
        button3.Enabled = false;
        button2.Enabled = true;
    }

}

 一些新式让人混淆的lamda匿名委托 伪协程 又像异步的东西

一些关于匿名委托 和lambda表达式的写法 ,还有异步调用:

  1 public void callDel(Func<int,int,int> dd)
  2         {
  3             int rst = dd.Invoke(5, 2);
  4             Console.WriteLine("call Func rst is:{0}", rst);
  5         }
  6 
  7         public int method1()
  8         {
  9             
 10             //delDone.WaitOne(2000);
 11             Thread.Sleep(2000);
 12             
 13             return 1;
 14         }
 15 
 16         public delegate int MyDele();
 17 ManualResetEvent delDone = new ManualResetEvent(false);
 18         private void Form3_Load(object sender, EventArgs e)
 19         {
 20 
 21             //waitone的作用 仅仅是阻断当前行代码以后的部分继续执行
 22             //然后在回调里证明已经回调已经完成了  继续放行
 23             //实际上是一种失去异步意义的一种方式 ,线程同步
 24             delDone.Reset();
 25             MyDele d4 = new MyDele(method1);
 26             d4.BeginInvoke((iar) =>
 27             {
 28                 int rdmResult = d4.EndInvoke(iar);
 29                 Console.WriteLine("iar call back state :" + iar.AsyncState.ToString());
 30                 Console.WriteLine("iar call back rst is :{0}", rdmResult);
 31                 delDone.Set();
 32             }, "ss");
 33             //delDone.Set();
 34             delDone.WaitOne(1000);
 35             Console.WriteLine("lalalala....");
 36 
 37             return;//----------------------------------------------------------------------------------------------------
 38             var cl = new {name="xiang",age=21};
 39             Console.WriteLine(cl.name);
 40             return;//-----------------------------------------------------------------------------------------------------
 41             //调用func  调用func 的直接好处就是 让你连委托声明 那句都省了
 42             //说白了就是你传一段代码进来 我来执行 ,那么你传的这段代码就是参数 。你只需要把方法原型作为形参定义好
 43             //感觉完全一锅写 有点类似于脚本语言的写法
 44             //上面是lambda写法 下面是 匿名委托写法
 45             callDel((num1, num2) => {
 46                 return num1 + num2;
 47             });
 48             callDel((num1, num2) => {
 49                 return num1 - num2;
 50             });
 51 
 52             callDel(delegate (int num1, int num2) {
 53                 return num1 * num2;
 54             });
 55 
 56             MyDele d2 = delegate ()
 57             {
 58                 Console.WriteLine("call myDel2....");
 59                 return 0;
 60             };
 61             d2();
 62 
 63             MyDele d3 = new MyDele(
 64             delegate {
 65                 Console.WriteLine("call myDel3....");
 66                 return 0;
 67             });
 68             d3();
 69 
 70             return;//-----------------------------------------------------------------------------------------------
 71             //异步回调
 72             MyDele d1 = new MyDele(() => {
 73                 //MessageBox.Show("mydel process");
 74                 Console.WriteLine("mydel start");
 75                 Thread.Sleep(2000);
 76                 Console.WriteLine("mydel end");
 77                 return new Random().Next();
 78             });
 79             //d1.Invoke();
 80 
 81             IAsyncResult iars= d1.BeginInvoke((iar) =>
 82             {
 83                 //委托内容执行完成 后的回调
 84 
 85                 //iar 可以理解为特定的线程的句柄 进行回溯的 找到我们当初调用的 那个方法
 86                 //从而获取到 方法执行的结果 ,endinvolve 代表实质性的异步调用结束 可获得实实在在的执行结果
 87                 //既然执行到这里了 那么自然代表可以正常endinvoke了。
 88                 int rdmResult = d1.EndInvoke(iar);
 89                 Console.WriteLine("iar call back state :" + iar.AsyncState.ToString());
 90                 Console.WriteLine("iar call back rst is :{0}",rdmResult);
 91             }, "ss");
 92 
 93             //执行到这里 你无法确定能否 IsCompleted 啊 ,如果强行endinvoke  不就市区异步的意义了么,
 94             //d1.EndInvoke(iars);
 95 
 96             //比如这里我们等待3秒钟 没有执行完成 就结束线程
 97 
 98             //wait one 就是阻塞主线程 把时间片完全让给 异步委托去执行 ,等待异步委托执行完成 。也是一种失去异步的意义的一种方式
 99             //waitone 的时间 并不代表一定需要执行那么长时间,如果不带时间参数表示等待直至异步线程完成
100             iars.AsyncWaitHandle.WaitOne();
101             Console.WriteLine("wait one 1000");
102             //Thread.Sleep(3000);
103             //if (iars.IsCompleted)
104             //    Console.WriteLine("mydel is complete");
105             //else
106             //{
107             //    Console.WriteLine("mydel is not complete");
108             //}
109             return;//---------------------------------------------------------------------------------------------------
110             //空参数 的lambda 委托写法 去掉action 一样的效果
111             Thread t1 = new Thread(new ThreadStart( new Action(()=>
112             {
113                 MessageBox.Show("t1 process");
114             })));
115             t1.Start();
116             return;//-----------------------------------------------------------------------------------------------------
117             //用lambda的方式来写事件绑定
118             button2.Click += new EventHandler((sender2, e2) =>
119             {
120                 MessageBox.Show("hellow");
121             });
122 
123             //Func 是一个带返回值形式  委托 ,就是微软新搞的一种花样  就是委托
124             //也可用lambda 的方式来写
125             List<string> strArr = new List<string>() {
126                 "xiang",
127                 "zhang",
128                 "chen"        };
129             //var sel = strArr.Where(wheretest);
130             var sel = strArr.Where((r) =>
131             {
132                 if (r.Contains("g"))
133                     return true;
134                 else
135                     return false;
136             });
137 
138             //lamda 方式的action
139             sel.ToList().ForEach(i =>
140             {
141                 Console.WriteLine(i);
142             });
143 
144             return;//-----------------------------------------------------------------------------------------------------
145             List<int> arr = new List<int>();
146             arr.Add(23);
147             arr.Add(45);
148             arr.Add(22);
149             arr.ForEach((i) =>
150            {
151                Console.WriteLine(i + "");
152            });
153         }

 每次看都有不一样的东西,看山不是山 ,人生就是不断的学习。以前以为异步调用就是简单的分一个通道来做事情 ,还真不是那么简单

 1 mydel d;
 2 private void button3_Click(object sender, EventArgs e)
 3 {
 4 
 5     d = new mydel(deldo);
 6     Console.WriteLine("aaa");
 7     //注意下面的d.BeginInvoke(null,null);并不一定要传入asyncCallBack ,根据你情况定 ,
 8     //比如你不需要得到异步调用的结果 (那还不如task.run() ? thread.start()?
 9     //注意 注意 注意 再次说明 这个asynCallBack 在异步方法调用完成后 才会执行 ,仅仅是一个获取结果用 并无特别意思
10 
11     //IAsyncResult ar= d.BeginInvoke(null,null);
12     //deldoend这个方法只有结束时才会调 ,只是相当于结束时候的一个通知 并没有其他意思
13     //如果不这样写 你就无法确定调用何时结束,当然你也可以 在begininvoke 后 同步做一些其他事, 
14     //在最终的时候使用ar.synchandle.waitOne 等待调用结束 ,由此来做到不同线程的同步
15     IAsyncResult ar = d.BeginInvoke(deldoEnd, "hhh");
16     //hhh为你想自定义传入end方法里的参数 end方法里使用ar.asyncState获取
17     Console.WriteLine("bbb");
18 
19     //Thread.Sleep(4000);
20     //Console.WriteLine("rstttt"+d.EndInvoke(ar));
21 
22 }
23 
24 public int deldo()
25 {
26     Console.WriteLine("ccc");
27     Thread.Sleep(3000);
28     Console.WriteLine(555);
29     return 5;
30 }
31 
32 public void deldoEnd(IAsyncResult ar)
33 {
34     Console.WriteLine("ddd");
35     int rest = d.EndInvoke(ar);
36     Console.WriteLine(ar.AsyncState.ToString());
37     Console.WriteLine(rest);
38 }
39 
40 delegate int mydel();

 又是一些新的Task特性的使用方式,还是线程只不过换了种方式:

https://www.cnblogs.com/lonelyxmas/p/9509298.html

https://www.cnblogs.com/jesse2013/p/async-and-await.html

https://www.cnblogs.com/CreateMyself/p/5983208.html

 一个简易的生产者消费者例子

今天闲得无聊 ,测试一下多线程的例子。

平常我们最简单的 list 链表 ,有个removeAt方法 最容易出错的地方是 倒序remove 其实每当remove一个后 count就自动减一了 。
为了保证索引不超出有时候还不如每次removeAt(0)。
接地系统到处用那种无脑的while true sleep方式 来刷新数据 展现 ,确实这种也是人能想到的最简单粗暴的方式 我们就不讨论那些什么事件通知 信号量玩意儿了。我们这里是一个例子。

 1 //共用数据存储位置
 2 List<int> ar = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8,9,10,11 };
 3 
 4 
 5 //各处生产数据的方法 每次lock占用 固定加10个数据
 6 //lock的对象在各操作方法多线程间只能被唯一访问
 7 public void method1(){
 8     lock (this)
 9     {
10         Random rdm = new Random();
11         for (int i = 0; i < 10; i++)
12         {
13             Thread.Sleep(10);
14             int beadd = rdm.Next(1, 100);
15             ar.Add(beadd);
16             Console.WriteLine("加入" + beadd);
17         }
18     }            
19 }
20 
21 //消费数据的方法
22 //无脑式的 一直运行 ,采用while 然后sleep一秒的方式 
23 //当然也可以while 不加sleep  不像单片机那样把资源占完 ,只是资源占用多总是不好的也没必要那么高密度检测
24 public void method2()
25 {
26     while (true)//为什么不lock此处 想也想得到啊 一直独占消费 你到底放不放权出去让别人生产数据啊
27     {
28         lock (this)//这个lock位置必须放在此处 ,一次占用直到退出消费10个数据(期间生产数据必须等待他完成 为了避免混乱)
29         {
30             if (ar.Count > 10)
31             {
32                 for (int i = 0; i < 10; i++)
33                 {
34                     Thread.Sleep(10);
35                     Console.WriteLine("移除" + ar[ar.Count - 1]);
36                     ar.RemoveAt(ar.Count - 1);
37                 }
38             }
39         }
40         Thread.Sleep(1000);
41     }           
42 }
1 //消费数据一直在后台运行 一有数据就会消费
2 Thread t2 = new Thread(method2);
3 t2.IsBackground = true;
4 //多线程的精妙之处 调用后会进入代码 去获权 或者称之为信号量 就是lock的那个东西
5 //获到权后 代码会自动往下走 总会执行 ,这一切是自动的 
6 method1();

 仿c#自带Beginxxx 异步结构 和 AutoResetEvent信号量使用

前段时间看一个原来网上下载的sock包装代码,看到里面把IAsyncResult包装了玩一种很花哨的套路,然后写成仿c#自带的beginxxx()  endxxx()的结构 ,为了研究明白 今天我们也来写这么一个类似结构。

  1 public class Class1
  2 {
  3     //异步测试入口
  4     public void AsyncTest()
  5     {
  6         //AutoResetEvent aa = new AutoResetEvent(true);
  7         ////跟嵌入式一样 set就是信号量置1,reset就是信号量置0
  8         //aa.Set();
  9         ////等待一个信号量在没等到时会一直阻塞于此
 10         ////while(true){} 也是类似作用 但是是一种很低级的线程间同步的手段
 11         ////因为while(true){} 会把时间片占完 WaitOne() 不会
 12         //aa.WaitOne();
 13 
 14         ////beginxxx  endxxx 本质还是伪多线程 ,只不过没有Thread.Start 这几个字写在你眼前而已
 15 
 16         byte[] data = new byte[20];
 17         IAsyncResult iar= Beginxx(data, CallBack, null);
 18         ////endxxx 相当于阻塞模式运行 阻断主线程 
 19         ////你看 是不是就类似于 c#原生的那种套路方式了?
 20         Endxxxx(iar);
 21         ////也可以不Endxxx 立即下面代码获取AsyncState结果 但是状态是未完成
 22         ////只能等begin代码自由运行结束 然后去触发Endxxx回调, 
 23         ////当你要手动Endxxx 那个代码点相当于意思是要进行同步了 手动归拢不同异步到一个时间点
 24         //Console.WriteLine(iar.AsyncState.ToString());
 25         Console.WriteLine("begin后立即调用时结果:"+Encoding.UTF8.GetString(data));
 26         Thread.Sleep(4000);
 27         Console.WriteLine("现在是" + Encoding.UTF8.GetString(data));
 28         //Console.WriteLine("现在的值是" + iar.AsyncState.ToString());
 29 
 30 
 31     }
 32     //异步方法begin
 33     //本质上传进去的 AsyncCallback这种类型的玩意儿 就只是一个method(IAsyncResult iar)这种带IAsyncResult参数的方法而已
 34     //IAsyncResult 参数是为了结束时 把执行结果传给回调函数 ,并没特别意思没那么神秘
 35     public IAsyncResult Beginxx(byte[] dataBeFull, AsyncCallback calback,object state)
 36     {
 37         //初始化一个自定义AsyncResult 完成为false
 38         MyAsyncResult sresult = new MyAsyncResult();
 39         sresult.IsCompleted = false;
 40         sresult.AsyncState = "aaa";
 41 
 42         //用Thread仿异步 执行 ,在执行完后设置IsCompleated为true 附上结果数据
 43         //并调用回调委托 传result进去
 44         Thread t = new Thread(() =>
 45         {
 46             Thread.Sleep(3000);
 47 
 48             sresult.IsCompleted = true;
 49             sresult.AsyncState = "bbb";
 50             byte[] bts = Encoding.UTF8.GetBytes("bbb");
 51 
 52             Array.Copy(bts, dataBeFull, bts.Length);
 53             calback(sresult);
 54         });
 55         t.IsBackground = true;
 56         t.Start();
 57 
 58         ////不用Thread 像下面这种代码 就没意思了哈 就不叫仿异步了
 59         ////跟流水线没什么区别
 60         //Thread.Sleep(2000);
 61         //sresult.IsCompleted = true;
 62         //sresult.AsyncState = "86547";
 63         //Console.WriteLine("ccc");
 64         //calback(sresult);
 65 
 66         return sresult;
 67     }
 68 
 69     //这里Endxxx 相当于是封装成 c#自带的Endxxx的样子
 70     public int Endxxxx(IAsyncResult iar)
 71     {
 72         while (iar.IsCompleted == false)
 73         {
 74             //如果这样一直while 不加sleep 会把时间片占完 并不友好 
 75             //像waitOne 这种类信号量的东西 就好的多 会把时间片让出来
 76             //这里只是为了演示同步异步的原理
 77             Thread.Sleep(500);
 78         }
 79 
 80         return 4684;
 81     }
 82 
 83     //这里的CallBack 相当于 是用户自己写的代码
 84     public void CallBack(IAsyncResult iar){
 85         
 86         int rsInt = Endxxxx(iar);
 87 
 88         Console.WriteLine("endxxx 的返回结果:"+rsInt);
 89 
 90         string rs = iar.AsyncState.ToString();
 91 
 92         Console.WriteLine(rs);
 93     }
 94 }
 95 
 96 public class MyAsyncResult : IAsyncResult
 97 {
 98 
 99     public object AsyncState
100     { get; set; }
101 
102     public WaitHandle AsyncWaitHandle
103     { get; set; }
104 
105     public bool CompletedSynchronously
106     { get; set; }
107 
108     public bool IsCompleted
109     { get; set; }
110 }

异步与界面更新的新方法

一直以为更新winform界面只能用Control.BeginInvoke ,今天无意中又发现一些新方式,总体来说是利用c#新特性的语法糖,但是依然有些坑,花样是新了 但是lamda表达式转换为匿名委托的过程中一些概念的理解很让人混淆。以下代码还是用于简易socket通信结构的 onReceive事件界面更新。

 1 SynchronizationContext synccontext = null;
 2 
 3 public Form1()
 4 {
 5     InitializeComponent();
 6     synccontext = SynchronizationContext.Current;
 7 }
 8 
 9 
10 private void Form1_Load(object sender, EventArgs e)
11 {
12 
13 
14     server.onReceive += new Action<Telegram_Base>((msg) =>
15     {
16         //synccontext.Post(UpdateTxt, msg);
17         this.BeginInvoke(
18             new Action(() =>
19             {
20                 UpdateTxt(msg.p);
21             })
22             ////总而言之 Invode 或者 BeginInvoke 需要一个确切类型的委托 光()=>{}只能代表一个匿名方法 系统不知道如何转换
23             ////Action就代表确切类型 ,但是 外面的(msg)=>{}不就代表 委托么为啥可以 真是费解 ,文章上说我们参数是具体的委托类型 就可以用匿名表达式
24 
25             ////这种也是可以 意思是新线程会自动去判断 界面UI资源使用冲突咯
26             ////等同于new thread().start()
27             //(ThreadStart)(() => {
28             //    UpdateTxt(msg);
29             //})
30 
31             ////文章上说
32             ////那么编译器在转化这个匿名函数的时候,就不知道要将这个匿名函数转为 ThreadStart还是MethodInvoker,于是报错
33             ////下面的代码也是可以的
34             //new MethodInvoker(
35             //delegate
36             //{
37             //    UpdateTxt(msg);
38             //})
39 
40             ////下面这种lamda 匿名委托的方式都不行的哈 ,只有action func 这种 是派生自Delegate的 metholdInvoker
41             ////()=>{}
42             ////delegate() { }
43             );
44     });
45 
46 }
47 
48 //最后,附上几个Delegate的原型:
49 //public delegate void ThreadStart();
50 //public delegate void MethodInvoker();
51 //public delegate void WaitCallback(object state);
52 //public delegate void ParameterizedThreadStart(object obj);
53 //public delegate void AsyncCallback(IAsyncResult ar);

 

posted @ 2012-11-06 19:03  assassinx  阅读(951)  评论(0)    收藏  举报