• C#容易忽略點--包含多線程 委託事件等等--此頁面bug,編輯能查看全部內容


    委託事件

    http://www.cnblogs.com/sdya/p/5217635.html

    反射

    1 创建用于反射使用的DLL

    新建一个C#类库项目,拷贝源代码如下,编译生成DLL(假如DLL的文件名是TestReflect.dll)

    using System;
     2
     3namespace Webtest
     4{
     5 /// <summary>
     6 /// ReflectTest 的摘要说明。
     7 /// </summary>
     8 public class ReflectTest
     9 {
    10  public ReflectTest()
    11  {}
    12
    13  public string WriteString(string s)
    14  {
    15   return "欢迎您," + s;
    16  }
    17
    18  /// <summary>
    19  /// dsajkjflasjdfalksdjfaskfd
    20  /// </summary>
    21  /// <param name="s"></param>
    22  /// <returns></returns>
    23  public static string WriteName(string s)
    24  {
    25   return "欢迎您光临," + s;
    26  }
    27
    28  public string WriteNoPara()
    29  {
    30   return "您使用的是无参数方法";
    31  }
    32 }
    33}
    34
    35
    36
    3.多線程的例子
    class Account
        {
            private Object thisLock = new object();
            int balance;
            Random r = new Random();

            public Account(int initial)
            {
                balance = initial;
            }

            int WithDraw(int amount)
            {
                if (balance < 0)
                {
                    throw new Exception("负的Balance.");
                }
                //确保只有一个线程使用资源,一个进入临界状态,使用对象互斥锁,10个启动了的线程不能全部执行该方法
                lock (thisLock)
                {
                    if (balance >= amount)
                    {
                        Console.WriteLine("----------------------------:" + System.Threading.Thread.CurrentThread.Name + "---------------");

                        Console.WriteLine("调用Withdrawal之前的Balance:" + balance);
                        Console.WriteLine("把Amount输入 Withdrawal     :-" + amount);
                        //如果没有加对象互斥锁,则可能10个线程都执行下面的减法,加减法所耗时间片段非常小,可能多个线程同时执行,出现负数。
                        balance = balance - amount;
                        Console.WriteLine("调用Withdrawal之后的Balance :" + balance);
                        return amount;
                    }
                    else
                    {
                        //最终结果
                        return 0;
                    }
                }
            }
            public void DoTransactions()
            {
                for (int i = 0; i < 100; i++)
                {
                    //生成balance的被减数amount的随机数
                    WithDraw(r.Next(1, 100));
                }
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                //初始化10个线程
                System.Threading.Thread[] threads = new System.Threading.Thread[10];
                //把balance初始化设定为1000
                Account acc = new Account(1000);
                for (int i = 0; i < 10; i++)
                {
                    System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(acc.DoTransactions));
                    threads[i] = t;
                    threads[i].Name = "Thread" + i.ToString();
                }
                for (int i = 0; i < 10; i++)
                {
                    threads[i].Start();
                }
                Console.ReadKey();
            }
        }

     --來自 http://www.cnblogs.com/knowledgesea/archive/2012/11/22/2780651.html

    1.进程:是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。

    2.线程:线程是程序中一个单一的顺序控制流程。是 程序执行流的最小单元。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源, 但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相 互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

    3.多线程:在单个程序中同时运行多个线程完成不同的工作,称为多线程。


    小结:其实更容易理解一点进程与线程的话,可 以举这样一个例子:把进程理解成为一个运营着的公司,然而每一个公司员工就可以叫做一个进程。每个公司至少要有一个员工,员工越多,如果你的管理合理的 话,公司的运营速度就会越好。这里官味一点话就是说。cpu大部分时间处于空闲时间,浪费了cpu资源,多线程可以让一个程序“同时”处理多个事情,提高 效率。


    单线程问题演示


    创建一个WinForm应用程序,这里出现的问题是,点击按钮后如果在弹出提示框之前,窗体是不能被拖动的。

    复制代码
     private void button1_Click(object sender, EventArgs e)
            {
                for (int i = 0; i < 10000000000; i++)  
                {
                    i += 1;
                }
                MessageBox.Show("出现后能拖动,提示没出现之前窗体不能被拖动");
            }
    复制代码

    原因:运行这个应用程序的时候,窗体应用程序自带一个叫做UI的线程,这个线程负责窗体界面的移动大小等。如果点击按钮则这个线程就去处理这个循环计算,而放弃了其它操作,故而窗体拖动无响应。这就是单线程带来的问题。

    解决办法:使用多线程,我们自己创建线程。把计算代码放入我们自己写的线程中,UI线程就能继续做他的界面响应了。


    线程的创建


     线程的实现:线程一定是要执行一段代码的,所以要产生一个线程,必须先为该线程写一个方法,这个方法中的代码,就是该线程中要执行的代码,然而启动线程时,是通过委托调用该方法的。线程启动是,调用传过来的委托,委托就会执行相应的方法,从而实现线程执行方法。

    复制代码
     //创建线程  
            private void button1_Click(object sender, EventArgs e)
            {
                //ThreadStart是一个无参无返回值的委托。
                ThreadStart ts = new ThreadStart(js);// ThreadStart ts = js; 一樣的效果
                //初始化Thread的新实例,并通过构造方法将委托ts做为参数赋初始值。
                Thread td = new Thread(ts);   //需要引入System.Threading命名空间
                //运行委托
                td.Start();
            }
            //创建的线程要执行的函数。
            void js()
            {
                for (int i = 0; i < 1000000000; i++)
                {
                    i += 1;
                }
                MessageBox.Show("提示出现前后窗体都能被拖动");
            }
    复制代码

    把这个计算写入自己写的线程中,就解决了单线程中的界面无反应缺陷。


    小结:创建线程的4个步骤:1.编写线程索要执行的方法。2.引用System.Threading命名空。3.实例化Thread类,并传入一个指向线程所要运行方法的委托。4.调用Start()方法,将该线程标记为可以运行的状态,但具体执行时间由cpu决定。


     方法重入(多个线程执行一个方法)


     由于线程可与同属一个进程的其它线程共享进程所拥有的全部资源。

    所以多个线程同时执行一个方法的情况是存在的,然而这里不经过处理的话会出现一点问题,线程之间先后争抢资源,致使数据计算结果错乱。 

    复制代码
     public partial class 方法重入 : Form
        {
            public 方法重入()
            {
                InitializeComponent();
    
                //设置TextBox类的这个属性是因为,开启ui线程,
                //微软设置检测不允许其它线程对ui线程的数据进行访问,这里我们把检测关闭,也就允许了其它线程对ui线程数据的访问。
                //如果检测不设置为False,则报错。
                TextBox.CheckForIllegalCrossThreadCalls = false;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                textBox1.Text = "0";
                //开启第一个线程,对js方法进行计算
                ThreadStart ts = new ThreadStart(js);
                Thread td = new Thread(ts);
                td.Start();
    
                //开启第二个线程,对js方法进行计算
                ThreadStart ts1 = new ThreadStart(js);
                Thread td1 = new Thread(ts1);
                td1.Start();
            }
            //多线程要重入的方法。
            void js()
            {
                int a = Convert.ToInt32(textBox1.Text);
                for (int i = 0; i < 2000; i++)
                {
                    a++;
                    textBox1.Text = a.ToString();
                }
            }
        } 
    复制代码

    出错现象:点击按钮后TextBox1中数据为2000+或2000,如果你看到的数据一直是2000说明你的计算机cpu比较牛X,这样的话你想 看到不是2000的话,你可以多点击几次试试,真不行的话,代码中给TextBox1赋值为0,换做在界面中给textBox1数值默认值为0试试看。

    出错原因:两个进程同时计算这个方法,不相干扰应该每个线程计算的结果都是2000的,但是这里的结果输出却让人以外,原因是第一个两个线程同时计 算,并不是同时开始计算,而是根据cpu决定的哪个先开始,哪个后开始,虽然相差时间不多,但后开始的就会取用先开始计算过的数据计算,这样就会导致计算 错乱。

    解决办法:解决这个的一个简单办法解释给方法加锁,加锁的意思就是第一个线程取用过这个资源完毕后,第二个线程再来取用此资源。形成排队效果。

    下面给方法加锁。

    复制代码
    //多线程要重入的方法,这里加锁。
            void js()
            {
                lock (this)
                {
                    int a = Convert.ToInt32(textBox1.Text);
                    for (int i = 0; i < 2000; i++)
                    {
                        a++;
                        textBox1.Text = a.ToString();
                    }
                }
            }
    复制代码

    给方法加过锁后,线程一前一后取用资源,就能避免不可预计的错乱结果,第一个线程计算为2000,第二个线程计算就是从2000开始,这里的结果就为4000。


    小结:多线程可以同时运行,提高了cpu的效率,这里的同时并不是同时开始,同时结束,他们的开始是由cpu决定的,时间相差不大,但会有不可预计的计算错乱,这里要注意类似上面例子导致的方法重入问题。


     前台线程后台线程


     .Net的公用语言运行时能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

    问题:关闭了窗口,消息框还能弹出。

    复制代码
      private void button1_Click(object sender, EventArgs e)
            { 
                //开启一个线程,对js方法进行计算
                ThreadStart ts2 = new ThreadStart(js);
                Thread td2 = new Thread(ts2);             
                td2.Start();
    
            }        
            void js()
            {
                for (int i = 0; i < 2000000000; i++)  //如果看不出效果这里的2后面多加0
                {
                    i++;
                }
                MessageBox.Show("关闭了窗口我还是要出来的!");
            }
    复制代码

    原因:.Net环境使用Thread建立线程,线程默认为前台线程。即线程属性IsBackground=false,而前台线程只要有一个在运行则应用程序不关闭,所以知道弹出消息框后应用程序才算关闭。

    解决办法:在代码中设置td2.IsBackground=true;


     线程执行带参数的方法


    复制代码
     //创建一个执行带参数方法的线程
            private void button1_Click(object sender, EventArgs e)
            {
                //ParameterizedThreadStart这是一个参数类型为object的委托
                ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
                Thread td2 = new Thread(pts);
                td2.Start("张三");  //参数值先入这里
            }
            void SayHello(object name)
            {
                MessageBox.Show("你好,"+name.ToString()+"!");
            } 
    复制代码

    线程执行带多参数的方法


     其实还是带一参数的方法,只不过是利用参数类型为object的好处,这里将类型传为list类型,貌似多参。

    复制代码
            //创建一个执行带多个参数的方法线程
            private void button1_Click(object sender, EventArgs e)
            {
                List<string> list = new List<string> { "张三", "李四", "王五" };
                //ParameterizedThreadStart这是一个参数类型为object的委托
                ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
                Thread td2 = new Thread(pts);
                td2.Start(list);  //参数值先入这里
            }
            void SayHello(object list)
            {
                List<string> lt = list as List<string>;
                for (int i = 0; i < lt.Count; i++)
                {
                    MessageBox.Show("你好," + lt[i].ToString() + "!");
                }
            } 
    复制代码


    总结:看到这里相信对多线程应该有一个初步的了解了,我就不说了。

    2 应用于反射的例子

    在ASPNET页面中加入以下函数:

    public void test1()
       {
      System.Reflection.Assembly ass;
      Type type ;
     object obj;
       try
      {
       ass = System.Reflection.Assembly.LoadFile(@"d:TestReflect.dll");
       type = ass.GetType("Webtest.ReflectTest");//必须使用名称空间+类名称
        System.Reflection.MethodInfo method = type.GetMethod("WriteString");//方法的名称
       obj = ass.CreateInstance("Webtest.ReflectTest");//必须使用名称空间+类名称
       string s = (string)method.Invoke(obj,new string[]{"jianglijun"}); //实例方法的调用
      
       Response.Write(s+"<br>");
       method = type.GetMethod("WriteName");//方法的名称
       s = (string)method.Invoke(null,new string[]{"jianglijun"}); //静态方法的调用
        Response.Write(s+"<br>");

       method = type.GetMethod("WriteNoPara");//无参数的实例方法
        s = (string)method.Invoke(obj,null);
        Response.Write(s+"<br>");
       method = null;
      }
     catch(Exception ex)
      {
       Response.Write(ex+"<br>");
    }33   }34  }35
    4.項目編碼
    C#中怎么设置编码 先说一下编码这块啊,不管你是HttpWebRequest还是Socket方法,都需要设置发送和接收时的编码格式,否则会出现乱码的 HttpWebRequest的解决方法大家可以参考我的文章:C#httphelper类 这里有解决编码的问题,也做到了无视编码的方式。 我们常用的编码有 utf-8,gb2312,gbk等这几种。 在C#中是通过一个类来确定的, 那就是 System.Text.Encoding 大部分情况下我们可以使用  System.Text.Encoding.UTF8来设置编码, 但是有时候也需要这个方法 System.Text.Encoding.GetEncoding("你的编码名称");
    怎么把字符转成相就的编码格式呢?
    这个更简单
    我们可以有两种方法
    第一种
    [C#] 纯文本查看 复制代码
    01
    Byte[] MyData = System.Text.Encoding.UTF8.GetBytes(“你的字符串”);
    第二种
    [C#] 纯文本查看 复制代码
    01
    System.Text.Encoding.GetEncoding("").GetBytes(“你的字符串”);

     例子

        class Program
        {
            private static object lockthis = new object();
            static void Main(string[] args)
            {
                ThreadStart ts = abc;
                Thread td = new Thread(ts);
                td.Start();
                ThreadStart tstwo = abc;
                Thread tdtwo = new Thread(tstwo);
                tdtwo.Start();
                Console.ReadKey();
            }

            public static void abc()
            {

                lock (lockthis)
                {
                for (int i = 1; i < 100; i++)
                {
                    Console.WriteLine(i);
                }
                }
            }
        }

    这种情况是对线程进行了上锁,那么会执行1-99,然后在执行1-99

        class Program
        {
            static void Main(string[] args)
            {
                ThreadStart ts = abc;
                Thread td = new Thread(ts);
                td.Start();
                ThreadStart tstwo = abc;
                Thread tdtwo = new Thread(tstwo);
                tdtwo.Start();
                Console.ReadKey();
            }

            public static void abc()
            {
                for (int i = 1; i < 100; i++)
                {
                    Console.WriteLine(i);
                }
               
            }
        }

    这里不加任何限制那么两个线程一起跑,那么打印出来的数字是没有规律的.

  • 相关阅读:
    数论练习
    AC自动机*
    矩阵乘法*
    概率期望*
    组合数学练习*
    图论升级*
    【终端使用】"su"命令切换用户
    【终端使用】"which"命令可以查看执行命令所在的位置
    【终端使用】"usermod"命令 和 组(包括:主组、附加组)
    Ubuntu 18.04安装 MySQL 8.0+版本的数据库
  • 原文地址:https://www.cnblogs.com/sdya/p/5220176.html
Copyright © 2020-2023  润新知