Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to tune under C# multithreading

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the relevant knowledge of "how to tune under C# multithreading". The editor shows you the operation process through an actual case. The operation method is simple, fast and practical. I hope this article "how to tune under C# multithreading" can help you solve the problem.

I. Atomic operation

Let's take a look at the problem code first.

/ get self-increment / public static void GetIncrement () {long result = 0; Console.WriteLine ("start calculation"); / / 10 concurrent execution Parallel.For (0,10, (I) = > {for (int j = 0; j)

< 10000; j++) { result++; } }); Console.WriteLine("结束计算"); Console.WriteLine($"result正确值应为:{10000 * 10}"); Console.WriteLine($"result 现值为:{result}"); Console.ReadLine(); }

This is the reason why the value of result is out of sync in multithreading.

1. Implementation based on Lock

Usually, what we use most should be locking, at the same time, only one thread enters the code block.

Implementation code:

Private static Object _ obj = new object (); / Atomic operation is based on Lock / public static void AtomicityForLock () {long result = 0; Console.WriteLine ("start calculation") / / 10 concurrent execution Parallel.For (0,10, (I) = > {/ / lock (_ obj) {for (int j = 0; j)

< 10000; j++) { result++; } } }); Console.WriteLine("结束计算"); Console.WriteLine($"result正确值应为:{10000 * 10}"); Console.WriteLine($"result 现值为:{result}"); Console.ReadLine(); } 结果:

two。 Implementation based on CAS

CAS is a famous lock-free algorithm. Lock-free programming, that is, the synchronization of variables between multithreads is realized without locks, that is, the synchronization of variables is realized without thread blocking.

The implementation class of CAS in .NET is Interlocked, which provides a lot of atomic operation methods, which eventually call Interlocked.CompareExchange (ref out, update value, expected value) / / operation based on memory barrier (seven steps).

When it comes to thread safety, don't think of locking at once, especially in situations that may be called frequently or require high performance.

CAS (Compare And Swap) compare and replace is a technique used when threads run concurrently.

CAS is an atomic operation that ensures concurrency security, but not concurrency synchronization.

CAS is an instruction of CPU (requires JNI to call the Native method in order to invoke the instruction of CPU)

CAS is a non-blocking, lightweight optimistic lock

Applicable scenarios for CAS

Read more and write less: if there are a large number of write operations, the CPU overhead may be too high because it will keep retry (spin) after a collision failure, which will consume CPU in the process.

Atomic operation of a single variable: what the CAS mechanism guarantees is the atomic operation of one variable, but not the atomicity of the whole code block. For example, if you need to ensure that three variables are updated together, you have to use pessimistic locks.

The main functions of Interlocked are as follows:

Interlocked.Increment atomic operation that increments the value of the specified variable and stores the result.

Interlocked.Decrement atomic operation that decrements the value of the specified variable and stores the result.

Interlocked.Add atomic operation, adding two integers and replacing the first integer with the sum of both

Interlocked.Exchange atomic operation, assignment

Interlocked.CompareExchange (ref a, b, c); atomic operation, a parameter is compared with c parameter, equal b replaces a, unequal does not replace. The return value of the method is always the original value of the first parameter, that is, the value in memory

Realize the above self-adding function with Interlocked.Increment

Code:

/ self-increment CAS implementation / public static void AtomicityForInterLock () {long result = 0; Console.WriteLine ("start calculation"); Parallel.For (0,10, (I) = > {for (int j = 0; j)

< 10000; j++) { //自增 Interlocked.Increment(ref result); } }); Console.WriteLine($"结束计算"); Console.WriteLine($"result正确值应为:{10000 * 10}"); Console.WriteLine($"result 现值为:{result}"); Console.ReadLine(); } 结果: Interlocked下原子操作的方法最终都是调用Interlocked.CompareExchange(ref a, b, c)实现的,现在我们利用CompareExchange自己实现一个原子操作功能 实现"一个变量自增到10000,然后又初始化到1开始自增的功能" 代码: /// /// 基于CAS原子操作自己写 /// public static void AtomicityForMyCalc() { long result = 0; Console.WriteLine("开始计算"); Parallel.For(0, 10, (i) =>

{long init = 0; long incrementNum = 0; for (int j = 0; j

< 10000; j++) { do { init = result; incrementNum = result + 1; incrementNum= incrementNum >

10000? 1: incrementNum; / / initializes to 1} / / if result=init is added to 10000, the value of result is replaced by incrementNum, otherwise result is unchanged, and the original value of result is returned, while (init! = Interlocked.CompareExchange (ref result, incrementNum, init)) If (incrementNum==10000) {Console.WriteLine ($"has increased to 10000! The value is initialized to 1 ");}); Console.WriteLine ($" end calculation "); Console.WriteLine ($" result correct value should be: {10000} "); Console.WriteLine ($" result present value: {result} "); Console.ReadLine ();}

Results:

3. Spin-locked SpinLock

Spin lock (spinlock):

It means that when a thread is acquiring a lock, if the lock has been acquired by another thread, the thread will wait in a loop, and then continue to determine whether the lock can be acquired successfully, and will not exit the loop until the lock is acquired.

When do you use a spin lock:

Spin locks are very helpful in avoiding blocking, but if you expect a lot of blocking, you probably shouldn't use spin locks because of too much rotation. Rotation can be helpful when locks are fine-grained and large (for example, one lock per node in the linked list) and when the lock holding time is always very short.

Spin lock (spinlock) is faster when locked for a short time. (because the spin lock essentially does not put the thread to sleep, but iterates to try to access the resource until it is available. So when a spin-locked thread is blocked, it does not switch the thread context, but idles and waits. For multicore CPU, the overhead of switching thread context is reduced, thus improving performance. If the machine is single-core or locked for a long time, avoid using it, because occupying the logical core will make other threads unavailable.

The difference between SpinLock and Lock:

SpinLock, spin lock. Whether the continuous check of the thread trying to acquire the lock can be obtained. At this point, the thread is still active, just idling, wasting cpu. However, spinlock avoids thread scheduling and context switching, and it is more efficient to use the lock if the lock time is very short.

And lock is the thread that has been block. This leads to behaviors such as thread scheduling and context switching.

Example:

/ / create spin lock private static SpinLock spin = new SpinLock (); public static void Spinklock () {Action action = () = > {bool lockTaken = false; try {/ / apply for acquisition lock spin.Enter (ref lockTaken) / / critical area for (int I = 0; I

< 10; i++) { Console.WriteLine($"当前线程{Thread.CurrentThread.ManagedThreadId.ToString()},输出:1"); } } finally { //工作完毕,或者产生异常时,检测一下当前线程是否占有锁,如果有了锁释放它 //避免出行死锁 if(lockTaken) { spin.Exit(); } } }; Action action2 = () =>

{bool lockTaken = false; try {/ / apply to acquire lock spin.Enter (ref lockTaken); / / critical section for (int I = 0; I

< 10; i++) { Console.WriteLine($"当前线程{Thread.CurrentThread.ManagedThreadId.ToString()},输出:2"); } } finally { //工作完毕,或者产生异常时,检测一下当前线程是否占有锁,如果有了锁释放它 //避免出行死锁 if (lockTaken) { spin.Exit(); } } }; //并行执行2个action Parallel.Invoke(action, action2); } 结果: 申请锁下面的临界区保证是顺序执行的,不会因为多线程穿插输出。 4.读写锁ReaderWriterLockSlim 读写锁是一个具有特殊用途的线程锁,适用于频繁读取且读取需要一定时间的场景,共享资源的读取操作通常是可以同时执行的, 普通的互斥锁不管是获取还是修改操作无法同时执行,如果多个线程为了读取操作而获取互斥锁,那么同一时间只有一个线程可以执行读取操作, 频繁读取的场景下会对吞吐量造成影响 读写锁把锁分为读取锁和写入锁,线程可以根据对共享资源的操作类型获取读取锁还是写入锁,读取锁可以被多个线程同时获取,写入锁不可以被多个线程同时获取,且读取锁和写入锁不可以被不同的线同时获取。 操作读取锁状态写入锁状态获取锁是否需要等待获取读取锁未被获取未被获取无需等待获取读取锁已被其他线程获取未获取无需等待获取读取锁未被获取已被其他线程获取需要等待其他线程释放获取写入锁未被获取未被获取无需等待获取写入锁已被其他线程获取未被获取需要等待其他线程释放获取写入锁未被获取已被其他线程获取需要等待其他线程释放 代码示例: //读写锁, //策略支持递归 private static ReaderWriterLockSlim rwl = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private static int index = 0; static void read() { try { //进入读锁 rwl.EnterReadLock(); for (int i = 0; i < 5; i++) { Console.WriteLine($"线程id:{Thread.CurrentThread.ManagedThreadId},读数据,读到index:{index}"); } } finally { //退出读锁 rwl.ExitReadLock(); } } static void write() { try { //尝试获写锁 while (!rwl.TryEnterWriteLock(50)) { Console.WriteLine($"线程id:{Thread.CurrentThread.ManagedThreadId},等待写锁"); } Console.WriteLine($"线程id:{Thread.CurrentThread.ManagedThreadId},获取到写锁"); for (int i = 0; i < 5; i++) { index++; Thread.Sleep(50); } Console.WriteLine($"线程id:{Thread.CurrentThread.ManagedThreadId},写操作完成"); } finally { //退出写锁 rwl.ExitWriteLock(); } } /// /// 执行多线程读写 /// public static void test() { var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None); Task[] task = new Task[6]; task[1] = taskFactory.StartNew(write); //写 task[0] = taskFactory.StartNew(read); //读 task[2] = taskFactory.StartNew(read); //读 task[3] = taskFactory.StartNew(write); //写 task[4] = taskFactory.StartNew(read); //读 task[5] = taskFactory.StartNew(read); //读 for (var i=0; i { int localItem; int localSum = 0; //取出并删除元素,先进先出 while (blocking.TryTake(out localItem)) { localSum += localItem; } //两数相加替换第一个值 Interlocked.Add(ref outerSum, localSum); }; //并行3个线程执行,多个线程同时取集合的数据 Parallel.Invoke(action, action, action); Console.WriteLine($"0+...{NUMITEMS-1} = {((NUMITEMS * (NUMITEMS - 1)) / 2)},输出结果:{outerSum}"); //此集合是否已标记为已完成添加且为空 Console.WriteLine($"线程安全集合.IsCompleted={blocking.IsCompleted}"); } } 结果: 限制集合长度示例: /// /// 限制集合长度 /// public static void BCLimtLength() { //限制集合长度为5个,后面进的会阻塞等集合少于5个再进来 BlockingCollection blocking = new BlockingCollection(5); var task1= Task.Run(() =>

{for (int I = 0; I)

< 20; i++) { blocking.Add(i); Console.WriteLine($"集合添加:({i})"); } blocking.CompleteAdding(); Console.WriteLine("完成添加"); }); // 延迟500ms执行等待先生产数据 var task2 = Task.Delay(500).ContinueWith((t) =>

{while (! blocking.IsCompleted) {var n = 0; if (blocking.TryTake (out n)) {Console.WriteLine ($"take out: ({n})") }} Console.WriteLine ("IsCompleted = true");}); Task.WaitAll (task1, task2);}

Results:

Example of using Stack (stack, first in, first out) in BlockingCollection:

/ Thread-safe collection, first in, then out, / public static void BCStack () {/ / Thread-safe collection. Parameters indicate stack identification. Queue length is 5 BlockingCollection blocking = new BlockingCollection (new ConcurrentStack (), 5); var task1 = Task.Run (()) = > {for (int I = 0; I)

< 20; i++) { blocking.Add(i); Console.WriteLine($"集合添加:({i})"); } blocking.CompleteAdding(); Console.WriteLine("完成添加"); }); // 等待先生产数据 var task2 = Task.Delay(500).ContinueWith((t) =>

{while (! blocking.IsCompleted) {var n = 0; if (blocking.TryTake (out n)) {Console.WriteLine ($"take out: ({n})") }} Console.WriteLine ("IsCompleted = true");}); Task.WaitAll (task1, task2);}

Enter 0-4 at the beginning, and start with the last 4.

two。 Thread safety dictionary

ConcurrentDictionary: this is easy to understand. Ordinary dictionaries will report errors when they are added with multiple threads, while this one is thread-safe and will not report errors.

Examples of general dictionaries:

/ / ordinary dictionary private static IDictionary Dictionaries {get; set;} = new Dictionary (); / public static void AddDictionaries () {Stopwatch sw = new Stopwatch (); sw.Start () / concurrent 1000 threads write Parallel.For (0, 1000, (I) = > {var key = $"key- {I}"; var value = $"value- {I}" / / error will be reported without locking / / lock (Dictionaries) / / {Dictionaries.Add (key, value); / /}}); sw.Stop (); Console.WriteLine ("current data volume of Dictionaries: {0}", Dictionaries.Count) Console.WriteLine ("Dictionaries execution time: {0} ms", sw.ElapsedMilliseconds);}

The result when unlocked:

After locking:

Example of a thread safety dictionary:

/ / Thread Safety Dictionary private static IDictionary ConcurrentDictionaries {get; set;} = new ConcurrentDictionary (); / Thread Safety Dictionary add value / public static void AddConcurrentDictionaries () {Stopwatch sw = new Stopwatch (); sw.Start () / concurrent 1000 threads write Parallel.For (0, 1000, (I) = > {var key = $"key- {I}"; var value = $"value- {I}"; / / No lock ConcurrentDictionaries.Add (key, value);}) Sw.Stop (); Console.WriteLine ("ConcurrentDictionaries current data volume is: {0}", ConcurrentDictionaries.Count); Console.WriteLine ("ConcurrentDictionaries execution time is: {0} ms", sw.ElapsedMilliseconds);}

As you can see, thread-safe dictionaries perform slightly better than ordinary dictionaries, and thread-safe dictionaries do not need to be locked.

Thread pool 1. Start the worker thread through QueueUserWorkItem

There are two overloaded static methods in the ThreadPool thread pool that start worker threads directly:

ThreadPool.QueueUserWorkItem (waitCallback)

ThreadPool.QueueUserWorkItem (waitCallback,Object)

Point the WaitCallback delegate to a no-return method with an Object parameter, and then use ThreadPool.QueueUserWorkItem (WaitCallback) to start the method in one step, where the parameters of the asynchronous method are treated as null.

Example 1:

Public class ThreadLoopDemo {/ callback method / static void CallMethod (object state) {Console.WriteLine ("RunWorkerThread starts working"); Console.WriteLine ("worker thread started successfully!") } public static void Test () {/ / maximum number of worker threads, ThreadPool.SetMaxThreads (1000, 1000) for the maximum number of worker threads; / / start worker thread ThreadPool.QueueUserWorkItem (new WaitCallback (CallMethod!)); Console.ReadKey ();}}

Execute the Test method, resulting in:

Example 2:

Using the second overloaded method ThreadPool.QueueUserWorkItem (WaitCallback,object) method, you can pass the object object as an argument to the callback function.

Public class ThreadLoopDemo {/ callback method / static void CallMethod (object state) {Console.WriteLine ("RunWorkerThread starts working"); Order order=state as Order; Console.WriteLine ($"orderName: {order.orderName}, price: {order.price}") Console.WriteLine ("worker thread started successfully!");} public static void Test () {/ / maximum number of worker threads, maximum number of ThreadPool.SetMaxThreads O threads (1000, 1000) Order order = new Order () {orderName = "refrigerator", price = 1888}; / / start worker thread ThreadPool.QueueUserWorkItem (new WaitCallback (CallMethod!), order); Console.ReadKey ();}} public class Order {public string orderName {get; set } public decimal price {get; set;}}

Execute the Test method, resulting in:

It's convenient to start a worker thread through ThreadPool.QueueUserWork, but the WaitCallback delegate must point to a no-return method with an object parameter.

Thread pools can also reuse threads, for example, you can set the thread pool size to 5 to perform a batch of tasks to prevent the creation of a large number of new threads from wasting a lot of cpu.

So the worker thread started by this method is only suitable for cases with a single parameter and no return value.

two。 Thread pool wait (semaphore)

ThreadPool doesn't have Thread's Join waiting interface, so do you want ThreadPool to wait to do so?

ManualResetEvent: notifies one or more waiting threads of an event that has occurred, equivalent to sending a signal

Mre.WaitOne: jam the current main thread and wait until the signal is changed to true before continuing to run.

Public class ThreadLoopDemo {/ execute / public static void Test () {/ / is used to control thread waiting. False defaults to the off state ManualResetEvent mre = new ManualResetEvent (false); ThreadPool.QueueUserWorkItem (p = > {Console.WriteLine ("Thread 1 starts") Thread.Sleep (1000); Console.WriteLine ($"Thread 1 ends with parameters, {Thread.CurrentThread.ManagedThreadId}"); / / notifies the thread that the modification signal is true mre.Set (); / / blocks the current thread until it signals that true is continuing to execute mre.WaitOne () / / close the thread, which is set to false mre.Reset (); Console.WriteLine ("signal is turned off"); ThreadPool.QueueUserWorkItem (p = > {Console.WriteLine ("Thread 2 starts"); Thread.Sleep (2000); Console.WriteLine ("Thread 2 ends") Mre.Set (); mre.WaitOne (); Console.WriteLine ("main thread ends");}}

Execute Test, and the result is:

3.Task

Task.Factory.StartNew: create a new thread, and the thread of Task is also taken from the thread pool (ThreadPool). It is officially recommended to use Task instead of using ThreadPool directly, because Task is an encapsulation and optimization of ThreadPool.

Task.WaitAny: wait for one of the threads in a group to finish. Here is the main thread of the card. Wait until the fastest thread in the group is completed before you can continue to execute. For example, get configuration information from three places (database, config,IO), and open three threads to access it at the same time.

Task.WaitAll: wait for all threads to finish, which is also the main thread of the card, waiting for all child threads to complete the task before continuing to execute.

Task.WhenAll: wait for all threads to finish. Here, don't jam the main thread. Wait for all the child threads to finish the task before you can proceed.

Task.ContinueWhenAny: the subsequent actions performed by any thread after completion in the form of a callback, which is similar to that of WaitAny, but not in the form of a callback.

Task.ContinueWhenAll: in the form of callback, the subsequent actions performed by all threads after completion, understand the same as above

Code example:

Public class TaskDemo {/ a time-consuming method that cycles 1000W times / public static void DoSomething (string name) {int iResult = 0; for (int I = 0; I)

< 1000000000; i++) { iResult += i; } Console.WriteLine($"{name},线程Id:{Thread.CurrentThread.ManagedThreadId},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")}"+Environment.NewLine); } public static void Test() { //线程容器 List taskList = new List(); Stopwatch watch = new Stopwatch(); watch.Start(); Console.WriteLine("************任务开始**************"); //启动5个线程 for (int i = 0; i < 5; i++) { string name = $"Task:{i}"; Task task = Task.Factory.StartNew(() =>

{DoSomething (name);}); taskList.Add (task) } / / in the form of callback, any subsequent action Task any = Task.Factory.ContinueWhenAny (taskList.ToArray (), task = > {Console.WriteLine ($"ContinueWhenAny, current thread Id: {Thread.CurrentThread.ManagedThreadId}) executed by a thread after completion continues to execute the following actions. {DateTime.Now.ToString ("yyyy-MM-dd HH:mm:ss ffff")} "+ Environment.NewLine) }) / / in callback form Subsequent actions Task all = Task.Factory.ContinueWhenAll (taskList.ToArray (), tasks = > {Console.WriteLine ($"ContinueWhenAll, current thread Id: {Thread.CurrentThread.ManagedThreadId}, callbacks completed by all threads, number of threads: {tasks.Length}, {DateTime.Now.ToString (" yyyy-MM-dd HH:mm:ss ffff ")}" + Environment.NewLine) }); / / put two callbacks into the container, including callbacks waiting for taskList.Add (any); taskList.Add (all); / / WaitAny: thread waits, and the current thread waits for one of the threads to finish Task.WaitAny (taskList.ToArray ()) Console.WriteLine ($"WaitAny, current thread Id: {Thread.CurrentThread.ManagedThreadId}, one of which is completed, {DateTime.Now.ToString (" yyyy-MM-dd HH:mm:ss ffff ")}" + Environment.NewLine); / / WaitAll: thread waits, current thread waits for completion of all threads Task.WaitAll (taskList.ToArray ()) Console.WriteLine ($"WaitAll, current thread Id: {Thread.CurrentThread.ManagedThreadId}, all threads complete, {DateTime.Now.ToString (" yyyy-MM-dd HH:mm:ss ffff ")}" + Environment.NewLine); Console.WriteLine ($"* task ends * time: {watch.ElapsedMilliseconds} ms, {Thread.CurrentThread.ManagedThreadId}, {DateTime.Now}") }}

Results:

4. Principle of thread pool scheduling

According to the difference of TaskCreationOptions, three branches appear

LongRunning: independent thread, independent of thread pool including PreferFairness: preferLocal=false, entering global queue without PreferFairness: preferLocal=ture, entering local queue

Tasks that enter the global queue can be fairly picked up and executed by threads in each thread pool.

In the following figure, Task1 first enters the global queue and is then led by Thread1. Thread3 steals Task3 in Thread2 through WorkStealing mechanism.

1. Thread Task 1 goes to the thread pool scheduling queue first.

2. The thread pool queue goes to the public queue or the thread's local queue according to the parameters.

Thread pool thread 1 fetches Task1 from the common thread pool to the local queue for execution

4. Thead3 finds that its queue is empty and the public queue is empty, so it steals task execution from other threads.

IV. Parallel

Parallel: is parallel programming, encapsulated on the basis of Task,. NET FrameWork version is available after 4.5, calling the parallelthread to participate in the execution of the task.

Different from Task: when you start a child thread with Task, the main thread is idle and does not participate in execution; when Parallel starts a child thread, the main thread will also participate in the calculation

Example 1:

/ A time-consuming method that cycles 1000W times / public static void DoSomething (string name) {int iResult = 0; for (int I = 0; I)

< 1000000000; i++) { iResult += i; } Console.WriteLine($"{name},线程Id:{Thread.CurrentThread.ManagedThreadId},{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")}" + Environment.NewLine); } public static void Test1() { //并行编程 Console.WriteLine($"并行编程开始,主线程Id:{Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine("【示例1】"); //示例1: //一次性执行1个或多个线程,效果类似:Task WaitAll,只不过Parallel的主线程也参与了计算 Parallel.Invoke( () =>

{DoSomething ("parallel 1-1"); () = > {DoSomething ("parallel 1-2");}, () = > {DoSomething ("parallel 1-3");}, () = > {DoSomething ("parallel 1-4");}, () = > {DoSomething ("parallel 1-5");}) Console.WriteLine ("* parallel end *"); Console.ReadLine ();}

The result of performing Test1:

Example 2:

Public static void Test2 () {/ / parallel programming Console.WriteLine ($"parallel programming starts, main thread Id: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine ("[example 2]") / / example 2: / / defines the number of threads to execute Parallel.For (0,5, t = > {int a = t; DoSomething ($"parallel 2-{a}");}) Console.WriteLine ("* parallel end *"); Console.ReadLine ();}

Results:

Example 3:

Public static void Test3 () {/ / parallel programming Console.WriteLine ($"parallel programming starts, main thread Id: {Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine ("[example 3]") ParallelOptions options = new ParallelOptions () {MaxDegreeOfParallelism = 3max / maximum number of concurrency of execution threads, one execution is completed, and then another} is opened. / / iterate through the collection and execute Parallel.ForEach (new List {"a", "b", "c", "d", "e", "f", "g"}, options, (t, status) = > {/ / status.Break ()) according to the number of threads in the collection; / / the end of this time. / / status.Stop (); / / the whole Parallel ends. Break and Stop cannot coexist DoSomething ($"parallel 4-{t}");});}

Results: divided into 3 times in parallel.

5. Asynchronous IO1. Comparison between Asynchronous IO and synchronous IO

Asynchronous IO does not block the main thread during the data preparation phase, while synchronous IO blocks the main thread.

two。 Read and write files asynchronously

The FileStream class is used here, which takes a parameter, useAsync, to avoid blocking threads in the thread pool in many cases. You can enable it or make parameter calls in the constructor through useAsync = true.

However, we cannot set the parameters in StreamReader and StreamWriter. However, if you want to use this parameter useAsync, you need to create a new FileStream object yourself.

Note that the asynchronous call is in UI, and even if the thread pool thread is blocked, the user interface thread will not be blocked during the await.

Write text asynchronously

/ / write asynchronously to the file / public async Task WriteTextAsync () {var path = "temp.txt"; / / File name var content = Guid.NewGuid () .ToString () / / write content using (var fs = new FileStream (path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true) {var buffer = Encoding.UTF8.GetBytes (content); await fs.WriteAsync (buffer, 0, buffer.Length);}}

After execution, view the root directory file results:

Read files asynchronously

/ asynchronously read text / public static async Task ReadTextAsync () {var fileName = "temp.txt" / / File name using (var fs = new FileStream (fileName, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None, bufferSize: 4096, useAsync: true) {var sb = new StringBuilder (); var buffer = new byte [4096]; var readLength = 0 While ((readLength = await fs.ReadAsync (buffer, 0, buffer.Length)! = 0) {var text = Encoding.UTF8.GetString (buffer, 0, readLength); sb.Append (text);} Console.WriteLine ("read file contents:" + sb.ToString ()) }}

Execution result:

This is the end of the content about "how to tune under C# multithreading". Thank you for your reading. If you want to know more about the industry, you can follow the industry information channel. The editor will update different knowledge points for you every day.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report