2016-11-14 5 views
0

C++에서 atomics를 사용할 때 포인터의 사용되지 않는 주소 비트에 다른 변수를 팩할 수 있습니다. 필자는 포인터와 함께 플래그, 카운터 등을 원자 적으로 변경할 수 있습니다. C#에서는 그렇게 쉬운 일이 아닙니다. 나는 특별한 것을 의미하는 정적 참조를 사용할 수 있다고 생각하지만, 힙을 압축 할 때 참조를 움직이는 것이 약간 걱정됩니다. 예를 들어,이 샘플 코드에서는 정적 참조 s_cantAddWork를 사용하여 추가하기 위해 닫힌 링크 된 목록을 원자 적으로 표시 할 수 있습니다. 내 질문은, 내가 gc 이동 s_cantAddWork 걱정해야합니까? 고정을 사용해야합니까? 지금까지 이와 같은 코드를 오랫동안 실행 한 후에는 안전하다고 판단됩니다. 그래도 전문가의 말을 듣고 싶습니다.원자 참조 목록에 정적 참조를 "플래그"로 사용할 수 있습니까?

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