Thread/ThreadPool/Task/TaskFactory
1. Difference between process and thread
A process is a program being executed, and each task executed in the process is a thread;
Thread belongs to process; Threads cannot be separated from delegates;
Netframework version 1.0:
Thread is a C# language encapsulation class for operating computer threads;
Thread code explanation
Thread thread = new Thread (); thread.Suspend();//suspend thread.Resume();//Let the paused thread continue execution thread.Abort();//Terminate thread thread.ResetAbort();//The terminated thread continues to execute; Static method; Thread.Sleep();//wait for thread.Join(2000);//Wait for a limited time, and wait for no time when it is out of date; while(thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(2000); } //thread priority thread.Priority = ThreadPriority.Highest;//It is best not to set it in this way; Only the priority is added conceptually, and the specific priority of the computer as a whole is subject to the reality; thread.IsBackgroud = true;//Set background thread; When the process closes, the thread is cancelled;
2. Single thread
Execute one by one in order
3. Multithreading
Work at the same time work at the same time work at the same time work at the same time work at the same time
1. High efficiency
2. High resource consumption
3. The execution sequence of each thread is difficult to control
4. Multithreaded callback
How to control the execution order of multithreads -- > callback (multithreaded instance: thread 2 needs to use the results in thread 1)
//There is no method to control the execution order of threads in Thread; But you can implement Thread callback by encapsulation //Multithreading instance: thread 2 needs to use the result in thread 1; private void main() { //1. Ordinary multithreading is executed in sequence ThreadStart threadStart = new ThreadStart (() => Console.WriteLine("Thread 1"); ); Action action = () => Console.WriteLine("Thread 2"); This.ThradWithCallBack(threadStart, action); //2. What if a return value is required? Func<int> func= () => //A delegate is defined { return DateTime.Now.Year; }; //int iResult = ThradWithReturn(func); Func<int> fResult = ThradWithReturn(func); int iResult = fResult.Invoke(); } //Multi thread callback using custom encapsulation private void ThradWithCallBack(ThreadStart threadStart,Action action) { //Start a thread to execute the two incoming threads; ThreadStart start = () =>{ threadStart.Invoke();//To execute thread action.Invoke(); }; new Thread(start).Start(); } private Func<T> ThradWithReturn<T>(Func<int> func) { T t = default(T); ThreadStart thradStart = () => { t = func.Invoke(); } ThreadStart threadStart = new ThreadStart (thradStart); threadStart.Start(); return new Func<T>(() => { //threadStart.join(1000);// Time limited waiting can be added, return t; }); }
5. Thread pool
There are many created threads in the thread pool. Take them out when you use them, and put them back into the thread pool when they are used up or not used up;
private void main() { Console.WriteLine("Thread 1-1"); //1. Ordinary multithreading is executed in sequence { WaitCallback waitCallback = new WaitCallback( o => Console.WriteLine("Thread 2-1"); ) ThreadPool.QueueUserWorkItem(waitCallback,"Thread pool");//Get thread from thread pool } //2. Callback (thread waiting) { ManualResetEvent mre = new ManualResetEvent(false);//Set the default value of the intermediate variable mre to false ThreadPool.QueueUserWorkItem(o => { Console.WriteLine("thread o") mre.Set(); });//Get thread from thread pool //Thread waiting mre.WaitOne();//Wait for the previous thread to execute first; } Console.WriteLine("Thread 1-1"); }
6,Task
6.1 what is the difference between task and TaskFactory?
Task is an encapsulated class for threads, which is actually a thread
TaskFactory: the place where the Task is created;
Summary: the Task comes from ThreadPool
private void main() { Console.WriteLine("Thread 1-1"); //1. How to apply for a thread { Task task = new Task(()=>{ Console.WriteLine("Thread 2"); }); task.Start(); } { TaskFactory taskFactory = new TaskFactory(); taskFactory.StartNew(() => { Console.WriteLine("Thread 3"); }); } { //Application thread ThreadPool.SetMaxThreads(8, 8);//Set the maximum number of threads, [global]; List<int> thradIds= new List<int>(); List<Task> tasklist = new List<Task>(); for(int i=0; i<50; i++) { tasklist.Add(Task.Run(() => {//Request a thread thradIds.Add("Get current thread Id"); Console.WriteLine("Current thread Id"); })) } Task.WaitAll(tasklist.ToArray());//Block the main thread and wait for all sub threads to complete the task before the main thread continues; (it will cause the main interface to jam) int iResult = threadIds.Distinct().Count(); //iResult=8;------------------------ //Summary: the Task comes from ThreadPool-------- } { List<Task> tasklist = new List<Task>(); //Single thread lectures Teach("Course 1");//Teach() user defined method Teach("Course 2"); Teach("Course 3"); Teach("Course 4"); Console.WriteLine("After the course teaching, prepare to assign tasks and write codes..."); //After the lecture, let multiple people code at the same time (multithreading) TaskFactory taskFactory = Task.Factory(); tasklist.Add(taskFactory.StaretNew(() => Coding("Classmate 1","back-stage management")));//Coding() user defined method; tasklist.Add(taskFactory.StaretNew(() => Coding("Classmate 2","Front page"))); tasklist.Add(taskFactory.StaretNew(() => Coding("Classmate 3","Construction of micro service architecture"))); tasklist.Add(taskFactory.StaretNew(() => Coding("Classmate 4","Applet interface implementation"))); //All the students have finished successfully. We are going to have a meal and K songs { //1.Task.WaitAll() Task.WaitAll(tasklist.ToArray());//Wait for all students to finish their work before the main thread continues to execute; It will cause interface jam in the main thread; Console.WriteLine($"After all the projects are completed, we are ready to have a dinner, K A song..."); } { //2.taskFactory.ContinueWhenAll() //Callback is used to apply for a new thread to execute a new action after a bunch of tasks are executed; taskFactory.ContinueWhenAll(tasklist.ToArray(),t => Console.WriteLine($"After all the projects are completed, we are ready to have a dinner, K A song...")); } { //3.taskFactory.ContinueWhenAny() when a task is executed, the main thread will continue to execute; taskFactory.ContinueWhenAny(tasklist.ToArray(),t => Console.WriteLine($"When a classmate completes his work, the teacher prepares for the construction of the environment")); } { //4. How to control the number of threads; //Why limit the number of threads? Because we should reasonably allocate computer system resources; List<Task> tasklist = new List<Task>(); for(int i=0 ; i<10000 ; i++) { int k = 1; if(tasklist.Count(t => t.Status == TaskStatus.Running) >= 20)//If the number of threads is 20, the main thread starts blocking and must wait until one thread completes before continuing with the main thread { Task.WaitAny(tasklist.ToArray()); //The following code debugs and adds conditions by itself; Not modified yet tasklist = tasklist.Where(t => t.Status != TaskStatus.RanToCompletion && t.Status != TaskStatus.Canceled && t.Status != TaskStatus.Faulted).ToList();//Retain threads that have not been executed; Whether rantocompletement() is completed; Cancelled; Faulted exception; } //Add a new thread tasklist.Add(Task.Run(()={ Thread.Sleep(2000); Console.WriteLine($"This is new Thread.");//It is easy to directly jam the computer; })); Console.WriteLine($"Number of threads is={tasklist.Count()}"); } } { //5. How can I get the return value if there is one in the newly applied thread? Func<int> func = ()=>{ //Define a delegate to return the current year; Thread.Sleep(3000); return DateTime.Now.Year; }; Task<int> task = new Task<int>(func); task.Start(); int i = task.Result;//This will block the page } } Console.WriteLine("Thread 1-1"); }
Task.WaitAll()
With taskfactory Continuewhenall() is similar, but task Waitall() will block the main thread and cause a jam; When all threads are finished, the main thread continues;
taskFactory.WaitAny()
The main thread is blocked. When one thread is executed, the main thread continues to execute;
taskFactory.ContinueWhenAll()
For example, a home page involves multiple data. Data 1 comes from DB, data 2 comes from a third party, and data 3 comes from other db;
Traditional order: search one by one, which is time-consuming; If each step takes an average of 1 second, the traditional sequence will take 3 seconds
When using ContinueWhenAll, the system will search at the same time, with a total time of 1 second; Improve query efficiency;
taskFactory.ContinueWhenAny()
Example: find a data;
Traditional search order: (1) find cache -- > (2) find interface -- > (3) find DB
When using ContinueWhenAny, multiple threads are created to search in different places at the same time. When one of them completes the search, the data can be returned directly and the page can be searched accordingly;
7. Expression tree expression < >
The expression directory tree is actually a data structure (binary tree).
Why use an expression tree?
Answer: expression tree – dynamic
Primary stage: during database query, SQL statements are spliced (one more condition splices one query string);
Now: use LinqToSql;
Expression<Func<int, int, int>> ex1 = (m, n) => m * n + 2 + 3;//As shown in the figure below
//1. Entrustment Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//(m, n) = > m * n + 2 is actually an anonymous method; Func < return value, m, n > var func1 = exp.Compile(); int IResult = func1.Invoke(1, 2); //2. Expression directory var peopleQuery = new List<People>().AsQueryable(); Expression<Func<People, bool>> expression = p => p.Id.Tostring().Equals("5"); PeopleQuery.Where(expression); //Compile //Decompile through ILspy //Look at the intermediate language. After parsing, it can be expressed as follows; ParameterExperssion p = Experssion.Paramter(typeof(People),"p"); FieldInfo fieldId = typeof(People).GetField("Id"); MemberExperssion exp = Experssion.Field(p,fieldId); MethodInfo toString = Typeof(People).GetField("Id"); var toString = Experssion.Call(exp ,toString ,Array.Empty<Expression>()); MethodInfo equals = typeof(string).GetMethod("Equals",new Type[] {typeof(string)}); Experssion expressionContant = Expression.Contant("5"); var equalExp = Expression.Call(toStringExp,equals, new Expression[]{ expressionContant });//equalExp == p.Id.Tostring().Equals("5") Expression<Func<People, bool>> expression = Expression.Lambda<Func<People, bool>>(equalExp, new ParamterExpression[]{ p });//expression == p => p.Id.Tostring().Equals("5") bool bResult = experssion.Compile().Invoke(new People(){Id = 5});//experssion.Compile() defines a delegate { }
7.1 how is the expression directory tree disassembled into Sql statements and recognized by the database
//ExpressionVisitor / / Visitor mode with Visitor //ExpressionVisitor. The visit () method is the entry to access the expression directory tree: 1. First, it will judge the type of expression directory (And, GreaterThan); 2. It will automatically call more professional methods for further access (expressionvisitor. Multiple specific partition method names ()); //ExpressionVisitor. Multiple concrete partition method names () / / convert them into SQL recognizable ones; Example 1: convert & & in C # to and in SQL; Example 2: only the minus sign is allowed in the program. Generally, at this time, we need to rewrite the Microsoft encapsulated method and change all the + signs to minus signs through artificial judgment; We can define a class ourselves to inherit from ExpressionVisitor,Then rewrite the encapsulation method; public static main() { Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;//Defines an expression directory tree; OperationsVisitor visitor = new OperationsVisitor(); Expression expNew = visitor.Modify(exp); } public class OperationsVisitor : ExpressionVisitor { public Expression Modify(Expression expression) { return this.Visitor(expression);//Call the Visitor method in ExpressionVisitor; Access to the expression tree } //1. Override the VisitBinary method in ExpressionVisitor protected override Expression VisitBinary(ExpressionBinary b)//VisitBinary corresponds to ExpressionBinary { if(b.NodeType == ExpressionType.Add) { Expression left = this.Visit(b.Left);//Recursion into VisitBinary() Expression right = this.Visit(b.Right); return Expression.Subtract(left, right); } return base.VisitBinary(b); } //Similarly, you can override methods in multiple base classes }
8.async / await syntax
The name of async modifier method;
When the main thread encounters await A, it returns. The content behind await A is executed by the new thread; When the thread of await A encounters await B, it is returned (await A is put back into the thread pool, indicating that A has finished its work), and the contents behind await B are executed by await B; Summary: the thread returns when it encounters await A
In the program, either await is not used, or it is used from beginning to end;
//When there is no async in the program and you want to use async;
await Task.CompletedTask();// Official framework recommendation
await Task.Delay();
Results: it can increase throughput and improve performance;
However: not all programs are applicable to async/await; as follows
Can await / async be concurrent? Is it sure to improve performance? What's the point?
Concurrency is not allowed in a single method. The thread returns when it encounters await, and the subsequent contents are executed by the new thread, which belongs to serial execution;
The performance cannot be improved, and the performance will be lower than that of synchronous calls. For example, for the same method, a single processing will not improve the performance, but only increase the load;
Meaning: 1. When multiple requests are processed concurrently and resources are limited, it can increase throughput (increase requests processed per unit time).
For example, the feeding time of a child is one hour, and * * when one teacher feeds four children (await in parallel), it is compared with one teacher feeding one child (ordinary serial) * *; In await parallel, when the teacher feeds the first child, the remaining three children eat by themselves, and then feed in turn. The final time will be less than 4 hours; In ordinary serial, the teacher feeds one by one, and finally it takes 4 hours;
ILSpy 5.x decompile dll file to view the source code
There is a state machine in ILSpy, which uses variables_ State means state machine mode: an object will have different behaviors according to different states (there is only one object, similar to traffic lights)
The difference between Task and await
Example: read local files;
Task mode - start 10 threads waiting to read files. The threads are always waiting and the cpu is always consumed;
await mode - in fact, the thread cannot be seen. Call the asynchronous API encapsulated by the system, initiate a request, and the thread will go back - the hardware will read it by itself, send a signal after reading, and then the thread pool will arrange the thread to process it;
Similar examples: call webApi, WCF, remote database link, connect to a third party and read cache; (both are worth using async)
Similar to pure CPU computing (not worth using async, but a waste of thread resources)
9. Reflection
9.1 principle and use of reflection
9.1. 1 principle of reflection
What is reflection?
A: the Reflection namespace is a help class library of Microsoft. You can read metadata and use the methods in the corresponding metadata;
High level language to machine recognition process: C# high level language -- > compiler compiles -- > DLL / exe (also contains metadata (similar to the list, only the method name can be seen), IL (intermediate language)) -- > CLR / JIT -- > machine code 010101
9.1. 2 use of reflection
Implementation in IOC;
//Basic class public class SqlServerHelper : IDBHelper { public SqlServerHelper() { } public void Query() { //Query content } } public Interface IDBHelper() { public void Query(); }
public static IDBHelper CreateInstance() { //1. Dynamically load DLL Assembly assembly = Assembly.Load("dll name");//LoadFrom() //2. Get type Type type = assembly.GetType("dll name.Corresponding Helper class A");//Helper class A inherits IDBHelper //3. Create object object odbHelper = Activator.CreateInstance(Type); //4. Convert the type to the corresponding Helper IDBHelper dbHelper = odbHelper.IDBHelper; //5. Call method dbHelper.Query(); }
9.1. 3 benefits and limitations of reflection
Why do you do this?
1. This can break the dependency on details (without adding namespace references)
2. Realize program configurability: the company came to the technical manager and asked to change the database to Mysql. If it is written in the ordinary way, it needs to change the code, recompile, republish and other troublesome steps; Through reflection, you only need to modify appsetting JSON, no need to change the code, recompile and other troublesome steps, and the program can be configured; (provided that the Helper classes of the two databases already exist, the steps of code modification, compilation and publishing are reduced)
3. Program extensibility is added (if the company only has sqlserver and mysqlhelper and does not have the helper class of orcale database, and now it needs to use orcale database, we only need to add a new orcale project + change the configuration file in the end;)
4. The realization is the core idea of IOC control inversion;
9.1. 3.2 limitations
9.1. 4 package
9.1. The program in 2 seems to need a lot of code, so we need to encapsulate it;
public class SimpleCreateInstance() { public Static IDBHelper CreateOnstance() { //(1) Basic package code Assembly assembly = Assembly.Load("dll name A");//LoadFrom() Type type = assembly.GetType("dll name A.Corresponding Helper class A");//These two steps can be optimized through the configuration file, as follows object odbHelper = Activator.CreateInstance(Type); IDBHelper dbHelper = odbHelper.IDBHelper; return dbHelper; //(2) Optimize packaging code //In the core, it is in appsetting JSON is configured through key value; //Configuration, such as: "dbhelperreflection": "DLL name A. corresponding Helper class A, dll name A.dll" string TypeName = CustomConfigManager.GetConfig("dbHelperRefliction").Split(',')[0]; //dll name A. corresponding Helper class A string DllName = CustomConfigManager.GetConfig("dbHelperRefliction").Split(',')[1]; //dll name A Assembly assembly = Assembly.Load(DllName);//LoadFrom() Type type = assembly.GetType(TypeName);//These two steps can be optimized through the configuration file, as follows object odbHelper = Activator.CreateInstance(Type); IDBHelper dbHelper = odbHelper.IDBHelper; return dbHelper; } } public static main() { IDBHelper idbHelper = SimpleCreateInstance.CreateInstance(); idbHelper.Query(); }
9.2 application of reflection in the project
Implementation in MVC:
localhost:8080/Home/Index
Why can the browser access this path to enter the Action? Finally, Action is called.
{ //---------------------------- //(1) Nonparametric public method Console.WriteLine("Reflection calls normal methods"); //1. Dynamically load dll Assembly assembly = Assembly.LoadForm(@"xxx.dll"); //2. Get type Type type = assembly.GetType(@"xxx.dll.Class name"); //3. Create object object oTest = Activator.CreateInstance(type); //How do methods call? //4. Acquisition method MethodInfo show1 = type.GetMethod("show1");//show1 is a custom parameterless method; //5. Use Invoke to call the method show1.Inoke(oTest,new object[0]);//Inoke needs to transfer two parameters, so we create an empty object; new object[0] is a new writing method; //Result: the call was successful //----------------------------- //(2) Public method with parameters MethodInfo show2 = type.GetMethod("show2");//Show2 is a custom parameterized method; show2(int id); show2.Inoke(oTest,new object[]{123});//success show2.Inoke(oTest,new object[]{"ZiFuChuan"});//Failure; Note: when passing parameters, you must pay attention to the consistency with the parameter type of the method; //------------------------------- //(3) Public overloaded method with parameters //show3(int id, string name); //show3(string name, int id); //show3(int id); //MethodInfo show3 = type.GetMethod("show3");// This method is wrong, because I don't know which specific method; MethodInfo show3 = type.GetMethod("show3", new Type[] {typeof(int), typeof(string)});//In this way, the show3(int id, string name) method is obtained show3.Inoke(oTest,new object[]{123, "ZiFuChuan"});//success //--------------------- //(4) * * private * * method with parameters //Show4 is a custom parameterized method; show4(string name); //MethodInfo show4 = type.GetMethod("show4");// Because it is private, it cannot be obtained in this way MethodInfo show4 = type.GetMethod("show4", BindingFlags.NonPublic | BindingFlags.Instance);//Pass the enumeration into NonPublic to find the private method; show4.Inoke(oTest,new object[]{"ZiFuChuan"}); //--------------------- //(5) Static method MethodInfo show5 = type.GetMethod("show5");//4. Acquisition method show5.Inoke(oTest,new object[]{"ZiFuChuan"});//success show5.Inoke(null,new object[]{"ZiFuChuan"});//success. In fact, static method calls do not require specific instances, but only "type. Static method name", so it is possible not to pass instances here. Basic knowledge of calling static methods; //--------------------- //(6) Generic methods for common classes //show6<T, W, X>(T t, W w, X x) //Common method call method: object Show6<int, string, DateTime>(123, "ZiFuChuan", DateTime.Now); //Common method call method: object Show6(123, "ZiFuChuan", DateTime.Now); The success of this method is determined by grammar sugar; MethodInfo show6 = type.GetMethod("show6");//4. Get generic method, successful //show6.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now});// fail MethodInfo showNew6 = show6.MakeGenericMethod(new Type[]{typeof(int), typeof(string), typeof(DateTime)});//MakeGenericMethod specifies the type showNew6.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now}); //--------------------- //(7) Generic method 1 of generic class //show7<T, W, X>(T t, W w, X x);;public class A<T, W, X> Assembly assembly = Assembly.LoadForm(@"xxx.dll"); //class here is A //Type type = assembly.GetType(@"xxx.dll.A");// Result: cannot get Type type = assembly.GetType(@"xxx.dll.A`3");//Results: successful` 3 indicates generic type, and there are 3 parameters; //object oTest = Activator.CreateInstance(type);// Failed because the normal method A obj = new A(); Is not allowed; You need to specify the generics in the class; Type typeNew = type.MakeGenericType(typeof(int), typeof(string), typeof(DateTime));//MakeGenericType specifies the type object oTest = Activator.CreateInstance(typeNew);//success MethodInfo show7 = typeNew.GetMethod("show7");//Successful because it is a class based generic; There is no need to specify the type again show7.Inoke(oTest,new object[]{123, "ZiFuChuan", DateTime.Now}); //--------------------- //(8) Generic methods of generic classes 2 //show8<T, W, X>(T t, W w, X x);; Public class a < T > / / + + + + + + note the generics of the classes here+++++++ Assembly assembly = Assembly.LoadForm(@"xxx.dll"); //class here is A Type type = assembly.GetType(@"xxx.dll.A`1");//Results: successful` 1 indicates generic type, with 1 parameter; Type typeNew = type.MakeGenericType(typeof(int), typeof(string), typeof(DateTime));//MakeGenericType specifies the type object oTest = Activator.CreateInstance(typeNew);//success MethodInfo show8 = typeNew.GetMethod("show8");//Successful, but the remaining 2 generic classes are not specified; Need to specify show8New = show8.MakeGenericMethod(new Type[]{typeof(int), typeof(DateTime)});//MakeGenericMethod specifies the type, and the remaining generics in the method are specified in order; show7.Inoke(oTest,new object[]{"ZiFuChuan", 123, DateTime.Now});//success }
Total: so when MVC requests a website, it will resolve the domain name, port, ControllerName and method Index step by step; In fact, it is positioned to a specific position by reflection;
9.3 reflective packaging framework, online handwritten ORM framework
9.3. 1 manipulate attributes or fields through reflection
{ ///1. Common mode People people = new People() { Id = 123, Name = "zifucahun", Age = 25 }; Console.WriteLine($"{people.Id}");//Value ///2. The implementation of reflection is as follows: Type type = typeof(People); object oPeople = Activator.CreateInstance(type);//Create instance //assignment foreach(var prop in type.GetProperties) { if(prop.Name.Equals("Id")) { prop.SetValue(oPeople, 123);//assignment } } //Value foreach(var field in type.GetFields()) { Console.WriteLine($"oPeople.{field.Name} = {field.GetValue(oPeople)}");//Traversal value } //Benefits of reflection //1. The code needs to be changed (when a field is added, deleted or updated) in the ordinary way, whether it is assignment or value taking //2. When the reflection value is taken, the code does not need to be changed, but the assigned value still needs to be modified; }
9.3. 2 implementation of using reflection in ORM framework
public class main { SqlServerHelper sqlServerHelper = new SqlServerHelper(); //Database tables corresponding to Student and Teacher models; Student s1 = sqlServerHelper.Query(1); Teacher t1 = sqlServerHelper.Query(1); } public class SqlServerHelper { //ORM, object-oriented idea. Essence: operate the database through the operation of the class (the fields and types of the class and the database must be consistent) Public T Query<T>(int id) //Public T Query<T>(string sqlstr) { //1. Database connection string string conn ="Data Souce=Host name; Database=Database name;User ID=sa;Password=Database login password; MultipleActiveResultSets=True;"; //2. Prepare SQl statements string sql = "";//+++++SQL statements can be split into external statements with greater flexibility++++ Type type = typeof(T);//Get the class name of generic T// Database table corresponding to generic t class; object oObj = Activator.CreateInstance(type);//Create the instance oObj corresponding to the generic T class //3. Connect to the database Using(SqlConnection connection = new SqlConnection(conn)) { connection.Open(); SqlCommand sqlCommand = new SqlCommend(sql, connection);//Connect SQL statements to the database; SqlDataReader reader = sqlCommand.ExecuteReader();//Ready to perform data reading reader.Read();//Read data //The following uses reflection; foreach(var prop in type.GetProperties())//Traverse all attributes of T { //Set the attribute value to the instance object oObj of generic T, and the attribute value is obtained from the reader index; value = reader [attribute name]; prop.SetValue(oObj,reader[prop.Name]);//Pay attention to the writing } } return (T)oObj;// return oObj as T;// Pay attention to the difference between the two ways of writing } //If you only want to query some fields, you can filter them through properties; }