C# 并发编程下性能优化的几大技巧

阅读时长 8 min read

在现代 Web 应用程序开发中,C# 并发编程成为了一个关键的技能。而在精神和时间上如此成本高昂的开发过程中,优化成为了你必须具备的养成性能的一部分。本文将介绍 C# 并发编程下的性能优化技巧,这些技巧将使您的代码更快,更可扩展,更健壮。

1. 多线程编程时的竞争条件

在多线程下运行代码时,一个常见的问题是竞态条件。当多个线程在读/写共享资源时,如果它们不被正确同步,就会导致数据无效或不一致。但是,防止各种竞争条件可以导致低效代码。

例如,考虑使用 lock 语句来避免竞争条件。 Lock 可以确保在同一时间只有一个线程可以进入指定的代码块,这可以避免一些竞争条件。但是,如果使用不当,lock 可能会阻塞其他线程并导致性能下降。以下是使用 lock 语句的示例代码:

-- -------------------- ---- -------
----- -------
-
  ------- ------ --- ------- - --
  ------ -------- ------ ------- - --- ---------
  
  ------ ---- ------------- -----
  -
    --- ------- - --- ---------------
    
    ------- - - -- - - -- ----
    -
      --- ------ - --- --------- --
      -
        ------- - - -- - - -------- ----
        -
          -------------------
        -
      ---
      --------------------
    -

    ----------------- -- -----------
    ----------------- -- ----------

    -------------------------- - - - ---------
    -------------------
  -

  ------ ---- ------------------
  -
    ---- ---------
    -
      ----------
    -
  -
-

上述代码使用一个静态的 object 变量来在所有线程之间共享锁定。虽然这段代码可以避免竞争条件,但是当所有线程都运行时,程序进入一种阻塞状态,因为线程互相阻塞以等待释放锁。

要解决这个问题,并在代码中更好地利用多线程处理能力,我们可以将计数器分割为多个块,每个块都有其自己的锁定对象。以下是示例代码:

-- -------------------- ---- -------
----- -------
-
  ------- ------ ----- ------------- - --- ---------
  ------ -------- -------- -------- - ------------------- ------------- -- --- --------------------
  
  ------ ---- ------------- -----
  -
    --- ------- - --- ---------------
    
    ------- - - -- - - -- ----
    -
      --- ------ - --- --------- --
      -
        ------- - - -- - - -------- ----
        -
          --- ----- - ------------
          -----------------------------
        -
      ---
      --------------------
    -

    ----------------- -- -----------
    ----------------- -- ----------

    --- ------- - --------------------
    -------------------------- - - - ---------
    -------------------
  -

  ------ --- ------------ ------
  -
    ------ ----- - ----
  -

  ------ ---- ------------------------- ------
  -
    ---- -----------------
    -
      -----------------------
    -
  -
-

2. 并发集合类的使用

对于并发编程,System.Collections.Concurrent 命名空间中有一些集合类可用,这些类是线程安全的。这些类包括 ConcurrentDictonary、ConcurrentQueue、ConcurrentStack 和 ConcurrentBag。使用这些类可避免手动同步代码,并且它们适用于高效的写操作。

例如,以下示例代码用于计算字符串数组中每个单词出现的次数。使用公共的线程安全 ConcurrentDictionary 类型保存单词计数。

-- -------------------- ---- -------
----- -------
-
  ------ ---- ------------- -----
  -
    --- --------- - -----
    -
      ----- -- --- ----- ----------
      ----- -- --- ------ ----------
      ----- -- --- ---- ---------
    --

    --- ---------- - --- ---------------------------- -------
    --------------------------- -------- --
    -
      --- ----- - -------------------- - - - -- ---------------------------------------
      ------- ---- ---- -- ------
      -
        ---------------------------- -- ----- --------- -- -------- - ---
      -
    ---
    -------------------------------- -- -------------
    -------------------
  -
-

3. 线程的使用

对于大多数情况下,使用 Task 类型来表示异步操作,可以方便地有效地使用不同的并行操作。例如,以下示例代码使用 Task.WhenAll 并行执行两个操作(下载并解析两个 XML 文件):

-- -------------------- ---- -------
----- -------
-
  ------ ----- ---- ------------- -----
  -
    --- ---- - -----
    -
      ----------------------------------------
      ---------------------------------------
    --

    --- ----- - --------------- -- ------------------------ -- -------------------------------------

    ----- --------------------
  -

  ------ ---- -------------------------- ----
  -
    ----- ---- ------ - --- ------------
    -
      ---------------------------
      -- ---
    -
  -
-

4. 惰性初始化

在使用并发编程时,可能会遇到需要延迟初始化的场景。对于惰性初始化应采用哪种方法,这取决于情况。以下标准方法描述了哪种惰性初始化方法更适合特定情况:

  • 对于线程安全集合的惰性生成,请使用 Lazy<T> 类型。例如:
-- -------------------- ---- -------
----- -------
----- ------------------------------

----- -------
-
    ------ --------------------------------- ----- ---- -
        --- --------------------------------- --------

    ------ ---- ------------- -----
    -
        ------------------------- ---
        -------------------------------------- 
    -
-
  • 对于非集合类型的懒惰初始化,请使用 Interlocked.CompareExchange。例如:
-- -------------------- ---- -------
----- -------
-
    ------- ------ --- ------
    ------- ------ --- ----------- - --

    ------ ---- ------------- -----
    -
        -------------
        -------------------------
        -------------------
    -

    ------ ---- ------------
    -
        -- ------------------------- ------------ -- -- --
        -
            ----- - ---
        -
    -
-

自从进行 C# 并发编程并不容易,我们需要不断学习和尝试。本文介绍了在 C# 并发编程下性能优化的一些技巧,希望本文的介绍对读者在后续的项目中提供有帮助的指导意义。

Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/67813af1935627c900b66fa3

Feed
back