-目录
在运用多线程技术之前,先得理解什么是线程。
那什么是线程呢?说到线程就不得不先说说进程。通俗的来讲,进程就是个应程序开始运行,那么这个应用程序就会存在一个属于这个应用程序的进程。
那么线程就是进程中的基本执行单元,每个进程中都至少存在着一个线程,这个线程是根据进程创建而创建的,所以这个线程我们称之为主线程。
那么多线程就是包含有除了主线程之外的其他线程。
如果一个线程可以执一个任务,那么多线程就是可以同时执行多个任务。
线程的基本用法:
-
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace 线程
- {
- public partial class Form1 : Form
- {
- private List
ThreadPool = new List(); -
- public Form1()
- {
- InitializeComponent();
- }
-
- private void Form1_Load(object sender, EventArgs e)
- {
- Thread thread = new Thread(TestThread);
- ThreadPool.Add(thread);
- thread.Start();
- }
-
- private void Form1_FormClosing(object sender, FormClosingEventArgs e)
- {
- if(ThreadPool.Count > 0)
- {
- foreach (Thread thread in ThreadPool)
- {
- if (thread.IsAlive)//当前线程是否终止
- {
- thread.Abort();//终止线程
- }
- }
- }
- }
-
- private void TestThread()
- {
- for (int i = 0; i <= 10; i++)
- {
- Console.WriteLine("输出:" + i);
- Thread.Sleep(500);
- }
- }
-
- private void Button_Test_Click(object sender, EventArgs e)
- {
- if (ThreadPool.Count > 0)
- {
- foreach (Thread thread in ThreadPool)
- {
- Console.WriteLine("当前线程是否终止:" + thread.IsAlive);
- }
- }
- }
- }
- }
-
- ///
- /// 计算md5
- ///
- ///
- ///
- private string CalcMD5(string str)
- {
- byte[] buffer = Encoding.UTF8.GetBytes(str);
- using (MD5 md5 = MD5.Create())
- {
- byte[] md5Bytes = md5.ComputeHash(buffer);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < md5Bytes.Length; i++)
- {
- sb.Append(md5Bytes[i].ToString("x2"));//X2时,生成字母大写MD5
- }
- return sb.ToString();
- }
- }
代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Utils
- {
- public class TimeInterval
- {
- ///
- /// 计算两个时间间隔的时长
- ///
- /// 返回的时间类型
- /// 开始时间
- /// 结束时间
- ///
返回间隔时间,间隔的时间类型根据参数 TimeType 区分 - public static double GetSpanTime(TimeType TimeType, DateTime StartTime, DateTime EndTime)
- {
- TimeSpan ts1 = new TimeSpan(StartTime.Ticks);
- TimeSpan ts2 = new TimeSpan(EndTime.Ticks);
- TimeSpan ts = ts1.Subtract(ts2).Duration();
- //TimeSpan ts = EndTime - StartTime;
-
- double result = 0f;
- switch (TimeType)
- {
- case TimeType.MilliSecond:
- result = ts.TotalMilliseconds;
- break;
- case TimeType.Seconds:
- result = ts.TotalSeconds;
- break;
- case TimeType.Minutes:
- result = ts.TotalMinutes;
- break;
- case TimeType.Hours:
- result = ts.TotalHours;
- break;
- case TimeType.Days:
- result = ts.TotalDays;
- break;
- }
- return result;
- }
- }
-
- ///
- /// 时间类型
- ///
- public enum TimeType
- {
- ///
- /// 毫秒
- ///
- MilliSecond,
- ///
- /// 秒
- ///
- Seconds,
- ///
- /// 分钟
- ///
- Minutes,
- ///
- /// 小时
- ///
- Hours,
- ///
- /// 天
- ///
- Days,
- ///
- /// 月
- ///
- Months
- }
- }
调用:
-
- class Program
- {
- static void Main(string[] args)
- {
- DateTime dateTime1 = DateTime.Now;
- Thread.Sleep(3000);
- DateTime dateTime2 = DateTime.Now;
-
- double timeDifference = TimeInterval.GetSpanTime(timeType, dateTime1, dateTime2);
- Console.WriteLine(string.Format("{0} {1}", timeDifference, timeType.ToString()));
-
- Console.ReadKey();
- }
- }
输出:
3 Seconds
新建一个C#控制台项目,要新建.NetFramework类型的,不然下面代码中的某些API无法使用。
代码:
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Test1
- {
- class Program
- {
- static void Main(string[] args)
- {
- List<double> resultList = ScaleConversion(new List<double>() { 230, 2453, 4353, 65 }, 10);
-
- string value = string.Empty;
- for (int i = 0; i < resultList.Count; i++)
- {
- value += string.Format("{0},", resultList[i]);
- }
- Console.WriteLine(value);
-
- Console.ReadKey();
- }
-
- ///
- /// 将数组等比例缩放
- ///
- /// 数组
- /// 缩放的最大值,最小值默认为0
- ///
缩放后的数组 - public static List<double> ScaleConversion(List<double> valueList, int maxScale)
- {
- if (valueList == null || valueList.Count == 0)
- {
- Console.WriteLine("valueList 不能为空");
- return null;
- }
- if (maxScale < 10)
- {
- Console.WriteLine("等比例的最大值不能小于10");
- return null;
- }
-
- double max = valueList.Max();
- double factor = Math.Round(max / maxScale, 2);//系数
- List<double> result = new List<double>();
- for (int i = 0; i < valueList.Count; i++)
- {
- result.Add(Math.Round(valueList[i] / factor, 2));
- }
-
- if (result.Count > 0)
- return result;
-
- return null;
- }
- }
- }
运行后,结果是:0.53,5.64,10,0.15,
这里是将10做为缩放比例中的最大值,有兴趣的读者可以自己算一下是否正确。
代码:
-
- using System;
-
- namespace Util
- {
- public class TimeCompute
- {
- ///
- /// 时间间隔
- ///
- /// 时间1
- /// 时间2
- ///
同一天的相隔的分钟的整数部分 - public static int DateDiff(DateTime DateTime1, DateTime DateTime2)
- {
- TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);
- TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);
- TimeSpan ts = ts1.Subtract(ts2).Duration();
- return Convert.ToInt32(ts.TotalMinutes);
- }
-
- ///
- /// 时间相加
- ///
- /// 时间1
- /// 时间2
- ///
时间和 - public static DateTime DateSum(DateTime DateTime1, DateTime DateTime2)
- {
- DateTime1 = DateTime1.AddHours(DateTime2.Hour);
- DateTime1 = DateTime1.AddMinutes(DateTime2.Minute);
- DateTime1 = DateTime1.AddSeconds(DateTime2.Second);
- return DateTime1;
- }
-
- ///
- /// 根据秒数得到 DateTime
- ///
- /// 秒数
- ///
以1970-01-01为日期的时间 - public static DateTime GetDateTimeBySeconds(double seconds)
- {
- return DateTime.Parse(DateTime.Now.ToString("1970-01-01 00:00:00")).AddSeconds(seconds);
- }
-
- ///
- /// 根据 DateTime 得到秒数
- ///
- /// 时间
- ///
秒数 - public static double GetDateTimeBySeconds(DateTime dateTime)
- {
- return (Convert.ToInt32(dateTime.Hour) * 3600) + (Convert.ToInt32(dateTime.Minute) * 60) + Convert.ToInt32(dateTime.Second);
- }
- }
- }
将字符串转换为时间
这里也可以使用 DateTime.Parse 进行转换,如果时间字符串写的不齐,只有时间,没有日期,就会以当前的日期和字符串中的时间进行组合
DateTime dt = Convert.ToDateTime("1:00:00");
两个时间相减
- DateTime t1 = DateTime.Parse("2007-01-01");
- DateTime t2 = DateTime.Parse("2006-01-01");
- TimeSpan t3 = t1 - t2;
代码
-
- //下面这两种字符串写法都可以
- string timer = "2022-02-02 18:15:58";
- string timer = "2022/2/18 18:18:26";
-
- DateTime dateTime = Convert.ToDateTime(timer);
- Console.WriteLine(dateTime);
在 项目 上 添加新项 选择“应用程序清单文件” 然后单击 添加 按钮

添加后,默认打开app.manifest文件,将:
修改为:


重新生成项目,再次打开程序时就会提示 需要以管理员权限运行。
代码:
- //任务
- Func<int> Funcs = () =>
- {
- Console.WriteLine("任务开始");
- return 1 + 1;
- };
-
- //执行任务
- Task<int> printRes = Task.Run(Funcs);
-
- //等待任务完成
- printRes.GetAwaiter().OnCompleted(() =>
- {
- Console.WriteLine("异步执行结果:" + printRes.Result);
- });
运行:
任务开始
异步执行结果:2
- string Chars = @"\";
- string Text = @"ddsdd\dddds";
- string[] Arr = Text.Split(new[] { Chars },StringSplitOptions.None);
-
- //结果:Arr[0] = "ddsdd, Arr[1] = dddds
1.发送字符串,这里的字符串可以为任意字符
- private void button1_Click(object sender,EventArgs e)
- {
- textBox1.Focus();
- SendKeys.Send("{A}");
- }
2.模拟组合键:CTRL + A
-
- private void button1_Click(object sender,EventArgs e)
- {
- webBrowser1.Focus();
- SendKeys.Send("^{A}");
- }
3.SendKeys.Send 异步模拟按键(不阻塞UI)
按键参考:虚拟键码 (Winuser.h) - Win32 apps | Microsoft Docs
-
- [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
- //bvk 按键的虚拟键值,如回车键为 vk_return, tab 键为 vk_tab(其他具体的参见附录)
- //bScan 扫描码,一般不用设置,用 0 代替就行;
- //dwFlags 选项标志,如果为 keydown 则置 0 即可,如果为 keyup 则设成 "KEYEVENTF_KEYUP";
- //dwExtraInfo 一般也是置 0 即可。
- public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
-
- private void button1_Click(object sender,EventArgs e)
- {
- textBox1.Focus();
- keybd_event(Keys.A, 0, 0, 0);
- }
4.模拟组合键:CTRL + A
- public const int KEYEVENTF_KEYUP = 2;
-
- private void button1_Click(object sender,EventArgs e)
- {
- webBrowser1.Focus();
- keybd_event(Keys.ControlKey,0,0,0);
- keybd_event(Keys.A,0,0,0);
- keybd_event(Keys.ControlKey,KEYEVENTF_KEYUP,0,0);
- }
5.上面两种方式都是全局范围呢,现在介绍如何对单个窗口进行模拟按键
下面代码没测试过,不知道有用没有
-
- [DllImport("user32.dll",EntryPoint = "PostMessageA",SetLastError = true)]
- public static extern int PostMessage(IntPtr hWnd,int Msg,Keys wParam,int lParam);
-
- public const int WM_CHAR = 256;
-
- private void button1_Click(object sender,EventArgs e)
- {
- textBox1.Focus();
- PostMessage(textBox1.Handle,256,Keys.A,2);
- }
- public const int WM_KEYDOWN = 256;
- public const int WM_KEYUP = 257;
-
- private void button1_Click(object sender,0)
- {
- PostMessage(webBrowser1.Handle,WM_KEYDOWN,0);
- }
这个功能在代码实现上比较简单,几行代码就可以做到
声明计时器
- System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
- stopwatch.Start();
暂停计时器,输出时间
-
- stopwatch.Stop();
- Console.WriteLine(stopwatch.Elapsed.Minutes + "分" + stopwatch.Elapsed.Seconds + "秒");
如果需要分段多次计时,那么就需要将计时器清空
-
- stopwatch.Stop();
- stopwatch.Reset();
- stopwatch.Start();
- public class Program
- {
- //定义类
- public class MyClass
- {
- public int Property1 { get; set; }
- }
-
- static void Main()
- {
- MyClass tmp_Class = new MyClass();
- tmp_Class.Property1 = 2;
- //获取类型
- Type type = tmp_Class.GetType();
- //获取指定名称的属性
- System.Reflection.PropertyInfo propertyInfo = type.GetProperty("Property1");
- //获取属性值
- int value_Old = (int)propertyInfo.GetValue(tmp_Class, null);
- Console.WriteLine(value_Old);
-
- //给对应属性赋值
- propertyInfo.SetValue(tmp_Class, 5, null);
- int value_New = (int)propertyInfo.GetValue(tmp_Class, null);
- Console.WriteLine(value_New);
- Console.ReadLine();
- }
- }
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace 特性
- {
- public class Program
- {
- static void Main(string[] args)
- {
- //获取程序集所有的类
- Type[] types = typeof(Program).Assembly.GetTypes();
- //遍历所有的类
- foreach (Type t in types)
- {
- //遍历当前类所有的方法
- foreach (MethodInfo method in t.GetMethods())
- {
- //遍历当前方法的上的所有特性
- foreach (Attribute attr in method.GetCustomAttributes())
- {
- //如果特性是GameSystem
- if (attr is GameSystem)
- {
- //实例化当前类的实例
- object reflectTest = Activator.CreateInstance(t);
- //获取当前方法的名字
- MethodInfo methodInfo = t.GetMethod(method.Name);
- //执行当前方法
- methodInfo.Invoke(reflectTest, null);
- }
- }
- }
- }
-
- Console.ReadKey();
- }
- }
-
- //自定义特性类
- [AttributeUsage(AttributeTargets.All)]
- public class GameSystem : Attribute//正常格式是GameSystemAttribute这样的
- {
- public GameSystem() { }
- }
-
- //自定义一个player类
- public class player
- {
- //GameSystem特性类的名字,如果定义的时候特性类的名字是这样GameSystemAttribute打上特性也是一样的
-
- [GameSystem]
- public void Start()
- {
- Console.WriteLine("GameSystem start");
- }
-
-
- [GameSystem]
- public void Updata()
- {
- Console.WriteLine("GameSystem updata");
- }
-
- public void Awaken()
- {
- Console.WriteLine("Awaken~~~~~~");
- }
- }
-
- }
运行:

- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Test_Console
- {
- class Program
- {
- static void Main(string[] args)
- {
- Test test = new Test();
- test.Print
().Run(); - test.Print
().Run(); - //test.Print
().ChickenFlew();//报错,因为没实现Movement接口 -
- Console.ReadKey();
- }
- }
-
- public class Test
- {
- private Dictionary<string, object> ObjectDictionary = new Dictionary<string, object>();
-
- public T Print<T>() where T: class, Movement, new()
- {
- Type t = typeof(T);
- string fullName = t.FullName;
- if (ObjectDictionary.ContainsKey(fullName))
- {
- return (T)ObjectDictionary[fullName];
- }
- else
- {
- object obj = Activator.CreateInstance(t);
- ObjectDictionary.Add(fullName, obj);
- return (T)obj;
- }
- }
- }
-
- public interface Movement
- {
- void Run();
- }
-
- public class Dog : Movement
- {
- public void Run()
- {
- Console.WriteLine("狗跑了");
- }
- }
-
- public class Cat : Movement
- {
- public void Run()
- {
- Console.WriteLine("猫跑了");
- }
- }
-
- public class Chook
- {
- public void ChickenFlew()
- {
- Console.WriteLine("鸡飞了");
- }
- }
- }
运行:

泛型的意义在于免去了类型之间互相转换的系统开销,和同类方法的重载,
比如,Add方法你要重载两个方法(int和double)或者更多方法,用范型只用写一个Add方法就可以完成int,double,float......等等的相加,
再如,集合的操作,没有往往是弱类型(object),而用范型可以直接是强类型,无需转换之间的开销,节省了资源,
代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace _01_自定义泛型
- {
- class Program
- {
- static void Main(string[] args)
- {
- // 泛型类
- MyClass1<string> myClass = new MyClass1<string>();
- myClass.SayHi("嘻嘻嘻");
-
- // 泛型委托
- MyGenericDelegate<string> md = m1;
- md("泛型委托");
-
- Console.ReadKey();
- }
-
- public static void m1(string msg)
- {
- Console.WriteLine(msg);
- }
-
- ///
- /// 泛型类
- ///
- ///
- public class MyClass1<T>
- {
- public void SayHi(T arg)
- {
- Console.WriteLine(arg);
- }
- }
-
- public class MyClass2
- {
- ///
- /// 泛型方法
- ///
- ///
- ///
- public void SayHi<T>(T msg)
- {
- Console.WriteLine(msg);
- }
- }
-
- ///
- /// 泛型接口
- ///
- ///
- public interface IFace<T>
- {
- void SayHi(T msg);
- }
-
- //-----实现泛型接口有两种情况-----
-
- ///
- /// 1.普通类实现泛型接口
- ///
- public class MyClass3 : IFace<string>
- {
- public void SayHi(string msg)
- {
- Console.WriteLine(msg);
- }
- }
-
- ///
- /// 2.泛型类继承泛型接口
- ///
- ///
- public class MyClass4<T> : IFace<T>
- {
- public void SayHi(T msg)
- {
- Console.WriteLine(msg);
- }
- }
-
- ///
- /// 泛型约束
- ///
- public class MyClass5<T,K,V,W,X>
- where T : struct // 约束T必须是值类型
- where K : class // 约束K必须是引用类型
- where V : IFace<T> // 约束V必须实现IFace接口
- where W: K // W必须是K类型,或者K类型的子类
- where X : class,new() // X必须是引用类型,并且有一个无参数的构造函数,当有多个约束时,new()必须写在最后
- {
- public void Add(T num)
- {
- Console.WriteLine(num);
- }
- }
-
- ///
- /// 泛型委托
- ///
- ///
- ///
- public delegate void MyGenericDelegate<T>(T args);
- }
- }
end