2016-11-14 6 views
0

При использовании атомистики в C++ я могу упаковать другие переменные в неиспользуемые биты адреса указателей. Я могу атомически менять флаги, счетчики и т. Д. Вместе с указателем. В C# это не так просто. Я думаю, что могу использовать статические ссылки, которые означают особые вещи, но я немного обеспокоен тем, что gc перемещает ссылки, когда он уплотняет кучу. Например, в этом примере кода можно атомно пометить связанный список как закрытый для добавления с помощью статической ссылки s_cantAddWork. Мой вопрос в том, нужно ли мне беспокоиться о том, что gc перемещает s_cantAddWork? Нужно ли использовать Fixed? До сих пор, после долгого запуска кода, это кажется безопасным. Я бы хотел услышать это от эксперта.Может ли статическая ссылка использоваться как «флаг» с атомно связанным списком?

using System; 
using System.Threading; 

namespace Testola { 
    class Program { 
     static void Main(string[] args) { 
      // pile = null 
      var w = new CWork(); 
      if (!AtomicAddWork(w)) 
       Console.WriteLine("Cant add work!"); // not hit 
      // pile = 1 
      w = new CWork(); 
      if (!AtomicAddWork(w)) 
       Console.WriteLine("Cant add work!"); // not hit 
      // pile = 2,1 
      w = AtomicGetWork(); 
      // pile = 1 
      w = new CWork(); 
      if (!AtomicAddWork(w)) 
       Console.WriteLine("Cant add work!"); // not hit 
      // pile = 3,1 

      // remove everything from pile and disable adding. 

      w = AtomicGetAllWorkAndLockOutOtherThreadsFromAddingMoreWork(); 
      // pile = s_cantAddWork 
      w = new CWork(); 
      if (!AtomicAddWork(w)) 
       Console.WriteLine("Cant add work!"); // HITS THIS! 
     } 

     public class CWork { 
      static int s_cItems = 0; 
      public static CWork s_cantAddWork = new CWork(); 
      public CWork next; 
      public string data = (s_cItems++).ToString(); 
     } 
     static volatile CWork m_workPile; 
     static bool AtomicAddWork(CWork work) { 
      while (true) { 
       var Old = m_workPile; 
       // WHAT HAPPENS HERE IF GC MOVES s_cantAddWork? <<------------------ 
       // I assume Old is moved too, and all threads are stopped, so my atomic stuff will still work. 
       if (Old == CWork.s_cantAddWork) 
        return false; 
       work.next = Old; 
       if (Interlocked.CompareExchange(ref m_workPile, work, Old) == Old) 
        return true; // success 
       work.next = null; 
      } 
     } 
     static CWork AtomicGetWork() { 
      while (true) { 
       var Old = m_workPile; 
       if (Old == null) 
        return null; 
       if (Interlocked.CompareExchange(ref m_workPile, Old.next, Old) == Old) 
        return Old; // success 
      } 
     } 
     static CWork AtomicGetAllWorkAndLockOutOtherThreadsFromAddingMoreWork() { 
      while (true) { 
       var Old = m_workPile; 
       if (Interlocked.CompareExchange(ref m_workPile, CWork.s_cantAddWork, Old) == Old) 
        return Old; // success 
      } 
     } 
    } 
} 
+1

Вам не нужно знать или заботиться, если GC движется что-то, если вы не работаете с небезопасным кодом. В вашем примере нет небезопасного кода. –

ответ

0

Это безопасно.

Ссылки не совпадают с указателями адресов. Когда C# перемещает память вокруг, будет прозрачным ваш код, если вы не используете небезопасные операции, такие как адрес оператора.

Чтобы получить адрес одноместного выражения, которое приводится к фиксированной переменной, используйте адрес-оператора: [MSDN]

int* p = &number; 

Если вы использовали адреса, то вам нужно будет отметить ваш код как небезопасный, используя ключевое слово unsage. В этот момент вы, вероятно, захотите привязать адрес, используя фиксированный statemnt.

Кроме того, обратите внимание на этот вопрос для более подробной информации:

How does the GC update references after compaction occurs