C#線程安全類型

来源:https://www.cnblogs.com/scmail81/archive/2018/08/20/9508785.html
-Advertisement-
Play Games

1、IProducerConsumerCollection (線程安全介面) 此介面的所有實現必須都啟用此介面的所有成員,若要從多個線程同時使用。 using System; using System.Collections; using System.Collections.Concurrent; ...


1、IProducerConsumerCollection (線程安全介面)
  此介面的所有實現必須都啟用此介面的所有成員,若要從多個線程同時使用。

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace ConsoleApp1
{
    public class SafeStack<T> : IProducerConsumerCollection<T>
    {
        // Used for enforcing thread-safety
        private object m_lockObject = new object();

        // We'll use a regular old Stack for our core operations
        private Stack<T> m_sequentialStack = null;

        //
        // Constructors
        //
        public SafeStack()
        {
            m_sequentialStack = new Stack<T>();
        }

        public SafeStack(IEnumerable<T> collection)
        {
            m_sequentialStack = new Stack<T>(collection);
        }

        //
        // Safe Push/Pop support
        //
        public void Push(T item)
        {
            lock (m_lockObject) m_sequentialStack.Push(item);
        }

        public bool TryPop(out T item)
        {
            bool rval = true;
            lock (m_lockObject)
            {
                if (m_sequentialStack.Count == 0) { item = default(T); rval = false; }
                else item = m_sequentialStack.Pop();
            }
            return rval;
        }

        //
        // IProducerConsumerCollection(T) support
        //
        public bool TryTake(out T item)
        {
            return TryPop(out item);
        }

        public bool TryAdd(T item)
        {
            Push(item);
            return true; // Push doesn't fail
        }

        public T[] ToArray()
        {
            T[] rval = null;
            lock (m_lockObject) rval = m_sequentialStack.ToArray();
            return rval;
        }

        public void CopyTo(T[] array, int index)
        {
            lock (m_lockObject) m_sequentialStack.CopyTo(array, index);
        }



        //
        // Support for IEnumerable(T)
        //
        public IEnumerator<T> GetEnumerator()
        {
            // The performance here will be unfortunate for large stacks,
            // but thread-safety is effectively implemented.
            Stack<T> stackCopy = null;
            lock (m_lockObject) stackCopy = new Stack<T>(m_sequentialStack);
            return stackCopy.GetEnumerator();
        }


        //
        // Support for IEnumerable
        //
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<T>)this).GetEnumerator();
        }

        // 
        // Support for ICollection
        //
        public bool IsSynchronized
        {
            get { return true; }
        }

        public object SyncRoot
        {
            get { return m_lockObject; }
        }

        public int Count
        {
            get { return m_sequentialStack.Count; }
        }

        public void CopyTo(Array array, int index)
        {
            lock (m_lockObject) ((ICollection)m_sequentialStack).CopyTo(array, index);
        }
    }
}
SafeStack
using System;
using System.Collections.Concurrent;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            TestSafeStack();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        // Test our implementation of IProducerConsumerCollection(T)
        // Demonstrates:
        //      IPCC(T).TryAdd()
        //      IPCC(T).TryTake()
        //      IPCC(T).CopyTo()
        static void TestSafeStack()
        {
            SafeStack<int> stack = new SafeStack<int>();
            IProducerConsumerCollection<int> ipcc = (IProducerConsumerCollection<int>)stack;

            // Test Push()/TryAdd()
            stack.Push(10); Console.WriteLine("Pushed 10");
            ipcc.TryAdd(20); Console.WriteLine("IPCC.TryAdded 20");
            stack.Push(15); Console.WriteLine("Pushed 15");

            int[] testArray = new int[3];

            // Try CopyTo() within boundaries
            try
            {
                ipcc.CopyTo(testArray, 0);
                Console.WriteLine("CopyTo() within boundaries worked, as expected");
            }
            catch (Exception e)
            {
                Console.WriteLine("CopyTo() within boundaries unexpectedly threw an exception: {0}", e.Message);
            }

            // Try CopyTo() that overflows
            try
            {
                ipcc.CopyTo(testArray, 1);
                Console.WriteLine("CopyTo() with index overflow worked, and it SHOULD NOT HAVE");
            }
            catch (Exception e)
            {
                Console.WriteLine("CopyTo() with index overflow threw an exception, as expected: {0}", e.Message);
            }

            // Test enumeration
            Console.Write("Enumeration (should be three items): ");
            foreach (int item in stack)
                Console.Write("{0} ", item);
            Console.WriteLine("");

            // Test TryPop()
            int popped = 0;
            if (stack.TryPop(out popped))
            {
                Console.WriteLine("Successfully popped {0}", popped);
            }
            else Console.WriteLine("FAILED to pop!!");

            // Test Count
            Console.WriteLine("stack count is {0}, should be 2", stack.Count);

            // Test TryTake()
            if (ipcc.TryTake(out popped))
            {
                Console.WriteLine("Successfully IPCC-TryTaked {0}", popped);
            }
            else Console.WriteLine("FAILED to IPCC.TryTake!!");
        }
    }
}
Program

2、ConcurrentStack類:安全堆棧

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = RunProgram();
            t.Wait();
        }

        static async Task RunProgram()
        {
            var taskStack = new ConcurrentStack<CustomTask>();
            var cts = new CancellationTokenSource();

            var taskSource = Task.Run(() => TaskProducer(taskStack));

            Task[] processors = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string processorId = i.ToString();
                processors[i - 1] = Task.Run(
                    () => TaskProcessor(taskStack, "Processor " + processorId, cts.Token));
            }

            await taskSource;
            cts.CancelAfter(TimeSpan.FromSeconds(2));

            await Task.WhenAll(processors);
        }

        static async Task TaskProducer(ConcurrentStack<CustomTask> stack)
        {
            for (int i = 1; i <= 20; i++)
            {
                await Task.Delay(50);
                var workItem = new CustomTask { Id = i };
                stack.Push(workItem);
                Console.WriteLine("Task {0} has been posted", workItem.Id);
            }
        }

        static async Task TaskProcessor(
            ConcurrentStack<CustomTask> stack, string name, CancellationToken token)
        {
            await GetRandomDelay();
            do
            {
                CustomTask workItem;
                bool popSuccesful = stack.TryPop(out workItem);
                if (popSuccesful)
                {
                    Console.WriteLine("Task {0} has been processed by {1}", workItem.Id, name);
                }

                await GetRandomDelay();
            }
            while (!token.IsCancellationRequested);
        }

        static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
            return Task.Delay(delay);
        }

        class CustomTask
        {
            public int Id { get; set; }
        }
    }
}
Program

3、ConcurrentQueue類:安全隊列

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = RunProgram();
            t.Wait();
        }

        static async Task RunProgram()
        {
            var taskQueue = new ConcurrentQueue<CustomTask>();
            var cts = new CancellationTokenSource();

            var taskSource = Task.Run(() => TaskProducer(taskQueue));

            Task[] processors = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string processorId = i.ToString();
                processors[i - 1] = Task.Run(
                    () => TaskProcessor(taskQueue, "Processor " + processorId, cts.Token));
            }

            await taskSource;
            cts.CancelAfter(TimeSpan.FromSeconds(2));

            await Task.WhenAll(processors);
        }

        static async Task TaskProducer(ConcurrentQueue<CustomTask> queue)
        {
            for (int i = 1; i <= 20; i++)
            {
                await Task.Delay(50);
                var workItem = new CustomTask { Id = i };
                queue.Enqueue(workItem);
                Console.WriteLine("插入Task {0} has been posted ThreadID={1}", workItem.Id, Thread.CurrentThread.ManagedThreadId);
            }
        }

        static async Task TaskProcessor(
            ConcurrentQueue<CustomTask> queue, string name, CancellationToken token)
        {
            CustomTask workItem;
            bool dequeueSuccesful = false;

            await GetRandomDelay();
            do
            {
                dequeueSuccesful = queue.TryDequeue(out workItem);
                if (dequeueSuccesful)
                {
                    Console.WriteLine("讀取Task {0} has been processed by {1} ThreadID={2}",
                                        workItem.Id, name, Thread.CurrentThread.ManagedThreadId);
                }

                await GetRandomDelay();
            }
            while (!token.IsCancellationRequested);
        }

        static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
            return Task.Delay(delay);
        }

        class CustomTask
        {
            public int Id { get; set; }
        }
    }
}
Program

4、ConcurrentDictionary類
  ConcurrentDictionary類寫操作比使用鎖的通常字典(Dictionary)要慢的多,而讀操作則要快些。因此對字典要大量的線程安全的讀操作,ConcurrentDictionary類是最好的選擇
  ConcurrentDictionary類的實現使用了細粒度鎖(fine-grained locking)技術,這在多線程寫入方面比使用鎖的通常的字典(也被稱為粗粒度鎖)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var concurrentDictionary = new ConcurrentDictionary<int, string>();
            var dictionary = new Dictionary<int, string>();

            var sw = new Stopwatch();

            sw.Start();
            for (int i = 0; i < 1000000; i++)
            {
                lock (dictionary)
                {
                    dictionary[i] = Item;
                }
            }
            sw.Stop();
            Console.WriteLine("Writing to dictionary with a lock: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                concurrentDictionary[i] = Item;
            }
            sw.Stop();
            Console.WriteLine("Writing to a concurrent dictionary: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                lock (dictionary)
                {
                    CurrentItem = dictionary[i];
                }
            }
            sw.Stop();
            Console.WriteLine("Reading from dictionary with a lock: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                CurrentItem = concurrentDictionary[i];
            }
            sw.Stop();
            Console.WriteLine("Reading from a concurrent dictionary: {0}", sw.Elapsed);
        }

        const string Item = "Dictionary item";
        public static string CurrentItem;
    }
}
Program

5、ConcurrentBag類

namespace ConsoleApp1
{
    class CrawlingTask
    {
        public string UrlToCrawl { get; set; }

        public string ProducerName { get; set; }
    }
}
CrawlingTask
using System.Collections.Generic;

namespace ConsoleApp1
{
    static class Module
    {
        public static Dictionary<string, string[]> _contentEmulation = new Dictionary<string, string[]>();

        public static void CreateLinks()
        {
            _contentEmulation["http://microsoft.com/"] = new[] { "http://microsoft.com/a.html", "http://microsoft.com/b.html" };
            _contentEmulation["http://microsoft.com/a.html"] = new[] { "http://microsoft.com/c.html", "http://microsoft.com/d.html" };
            _contentEmulation["http://microsoft.com/b.html"] = new[] { "http://microsoft.com/e.html" };

            _contentEmulation["http://google.com/"] = new[] { "http://google.com/a.html", "http://google.com/b.html" };
            _contentEmulation["http://google.com/a.html"] = new[] { "http://google.com/c.html", "http://google.com/d.html" };
            _contentEmulation["http://google.com/b.html"] = new[] { "http://google.com/e.html", "http://google.com/f.html" };
            _contentEmulation["http://google.com/c.html"] = new[] { "http://google.com/h.html", "http://google.com/i.html" };

            _contentEmulation["http://facebook.com/"] = new[] { "http://facebook.com/a.html", "http://facebook.com/b.html" };
            _contentEmulation["http://facebook.com/a.html"] = new[] { "http://facebook.com/c.html", "http://facebook.com/d.html" };
            _contentEmulation["http://facebook.com/b.html"] = new[] { "http://facebook.com/e.html" };

            _contentEmulation["http://twitter.com/"] = new[] { "http://twitter.com/a.html", "http://twitter.com/b.html" };
            _contentEmulation["http://twitter.com/a.html"] = new[] { "http://twitter.com/c.html", "http://twitter.com/d.html" };
            _contentEmulation["http://twitter.com/b.html"] = new[] { "http://twitter.com/e.html" };
            _contentEmulation["http://twitter.com/c.html"] = new[] { "http://twitter.com/f.html", "http://twitter.com/g.html" };
            _contentEmulation["http://twitter.com/d.html"] = new[] { "http://twitter.com/h.html" };
            _contentEmulation["http://twitter.com/e.html"] = new[] { "http://twitter.com/i.html" };
        }
    }
}
Module
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Module.CreateLinks();
            Task t = RunProgram();
            t.Wait();
        }

        static async Task RunProgram()
        {
            var bag = new ConcurrentBag<CrawlingTask>();

            string[] urls = new[] { "http://microsoft.com/", "http://google.com/", "http://facebook.com/", "http://twitter.com/" };

            var crawlers = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string crawlerName = "Crawler " + i.ToString();
                bag.Add(new CrawlingTask { UrlToCrawl = urls[i - 1], ProducerName = "root" });
                crawlers[i - 1] = Task.Run(() => Crawl(bag, crawlerName));
            }

            await Task.WhenAll(crawlers);
        }

        static async Task Crawl(ConcurrentBag<CrawlingTask> bag, string crawlerName)
        {
            CrawlingTask task;
            //嘗試從bag中取出對象
            while (bag.TryTake(out task))
            {
                IEnumerable<string> urls = await GetLinksFromContent(task);
                if (urls != null)
                {
                    foreach (var url in urls)
                    {
                        var t = new CrawlingTask
                        {
                            UrlToCrawl = url,
                            ProducerName = crawlerName
                        };
                        //將子集插入到bag中 
                        bag.Add(t);
                    }
                }
                Console.WriteLine("Indexing url {0} posted by {1} is completed by {2}!",
                    task.UrlToCrawl, task.ProducerName, crawlerName);
            }
        }

        static async Task<IEnumerable<string>> GetLinksFromContent(CrawlingTask task)
        {
            await GetRandomDelay();

            if (Module._contentEmulation.ContainsKey(task.UrlToCrawl)) return Module._contentEmulation[task.UrlToCrawl];

            return null;
        }

        static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(150, 200);
            return Task.Delay(delay);
        }


    }
}
Program

6、BlockingCollection類
  BlockingCollection類: 我們能夠改變任務存儲在阻塞集合中的方式。預設情況下它使用的是ConcurrentQueue容器,但是我們能夠使用任何實現了IProducerConsumerCollection泛型介面的集合。

namespace ConsoleApp1
{
    class CustomTask
    {
        public int Id { get; set; }
    }
}
CustomTask
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    static class Module
    {
        public static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
            return Task.Delay(delay);
        }
    }
}
Module
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Using a Queue inside of BlockingCollection");
            Console.WriteLine();
            Task t = RunProgram();
            t.Wait();

            //Console.WriteLine();
            //Console.WriteLine("Using a Stack inside of BlockingCollection");
            //Console.WriteLine();
            //Task t = RunProgram(new ConcurrentStack<CustomTask>());
            //t.Wait();
        }

        static async Task RunProgram(IProducerConsumerCollection<CustomTask> collection = null)
        {
            var taskCollection = new BlockingCollection<CustomTask>();
            if (collection != null)
                taskCollection = new BlockingCollection<CustomTask>(collection);
            //初始化collection中的數據
            var taskSource = Task.Run(() => TaskProducer(taskCollection));

            Task[] processors = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string processorId = "Processor " + i;
                processors[i - 1] = Task.Run(
                    () => TaskProcessor(taskCollection, processorId));
            }

            await taskSource;

            await Task.WhenAll(processors);
        }
        /// <summary>
        /// 初始化collection中的數據
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        static async Task TaskProducer(BlockingCollection<CustomTask> collection)
        {
            for (int i = 1; i <= 20; i++)
            {
                await Task.Delay(20);
                var workItem = new CustomTask { Id = i };
                collection.Add(workItem);
                Console.WriteLine("Task {0} has been posted", workItem.Id);
            }
            collection.CompleteAdding();
        }
        /// <summary>
        /// 列印collection中的數據
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        static async Task TaskProcessor(
            BlockingCollection<CustomTask> collection, string name)
        {
            await Module.GetRandomDelay();
            foreach (CustomTask item in collection.GetConsumingEnumerable())
            {
                Console.WriteLine("Task {0} has been processed by {1}", item.Id, name);
                await Module.GetRandomDelay();
            }
        }
    }
}
Program

7、使用ThreadStatic特性
  ThreadStatic特性是最簡單的TLS使用,且只支持靜態欄位,只需要在欄位上標記這個特性就可以了

using System;


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 體會 第一次開始學習Python,不知道自己之後能否持續堅持下去。我想不論怎樣只要有時間和就努力堅持。爭取以周為單位按要求完成相應工作量的學習。持續學習,不斷總結和進步。 一、Python安裝 Python的安裝是還是是一件比較麻煩的事情。之前安裝python環境到還比較簡單一些。後面再安裝PyCh ...
  • 1、線程的實現方式 線程有兩種實現方式,分別為繼承Thread、實現Runnable介面。差異性:實現方式避免了類的繼承單一性,且對於多個線程同時訪問同一個資源時更便捷。 (1)繼承Thread class TestThread extends Thread { @Override public v ...
  • 1 from lxml import etree 2 from selenium import webdriver 3 import pymysql 4 5 def Geturl(fullurl):#獲取每個招聘網頁的鏈接 6 browser.get(fullurl) 7 shouye_html_t... ...
  • 開始接觸php,奇怪的是,每次打PHP的時候,都會出現,拍*黃*片,覺得好搞笑 今天聽了一上午的課,老司機講的是,面向對象,世間萬物皆對象~ 以下是上課的筆記,和大家共用,不謝,多練習~ //面向對象 // 1 世間萬物皆對象 //一個人,一個車,一架飛機,一個垃圾桶 一切可見之物都是對象,一切不可 ...
  • 數組左移 i 位 3 種方法 1.臨時數組存儲 先將前 i 個元素用數組存起來 再將後 n - i 個元素左移 i 位 最後將存起來的數組添加到後面去即可 2.通過多次調用左移 1 位的函數 3.翻轉 將待移動的數組以 i 為分隔看成兩段 AB 先將 A 翻轉,再將 B 翻轉 之後將數組整個翻轉 1 ...
  • 目錄: 一、os模塊中關於文件/目錄常用的函數使用方法 二、os.path模塊中關於路徑常用的函數使用方法 三、課時30課後習題及答案 接下來會介紹跟Python的文件相關的一些很有用的模塊。模塊是什麼?其實我們寫的每一個源代碼文件(*.py)都是一個模塊。Python自身帶有非常多使用的模塊。 比 ...
  • 想成為一位合格的Android程式員或者一位Android高級工程師是十分有必要知道Android的框架層的工作原理,要知道其工作原理那麼就需要閱讀Android的源代碼。 想要閱讀Android的源代碼最爽的方式的肯定是自己親手下載編譯。為什麼要閱讀Android源代碼? 第一,面試加分項。第二, ...
  • 一. 說在前面的話 凡是大約工作在兩年以上的朋友們,或多或少都會接觸到一些框架搭建方面的知識,只要一談到框架搭建這個問題或者最佳用法這個問題,勢必會引起一點點小小的風波,我說我的好,他說他的好,非常容易罵架,所以在本節乃至該系列我僅僅是總結了一下自己日常中的一些用法,談一下自己淺陋的見解,談不上最佳 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...