如何创建FIFO /强信号量

时间:2014-05-01 20:07:14

标签: c# .net multithreading concurrency

我需要在C#中编写自己的FIFO /强信号量,使用我自己的信号量作为基础。我发现this example,但它不太对,因为我不应该使用Monitor.Enter / Exit。

这些是我的常规信号量的方法,我想知道是否有一种简单的方法可以使它适应FIFO。

public virtual void Acquire()
{

    lock (this)
    {

        while (uintTokens == 0)
        {

            Monitor.Wait(this);

        }

        uintTokens--;

    }

}

public virtual void Release(uint tokens = 1)
{

    lock (this)
    {

        uintTokens += tokens;
        Monitor.PulseAll(this);

    }

}

2 个答案:

答案 0 :(得分:15)

所以SemaphoreSlim为我们提供了一个良好的起点,所以我们首先将其中一个包装在一个新类中,然后将等待方法中的所有内容指向该信号量。

要获得类似行为的队列,我们​​需要一个队列对象,并确保在面对多线程访问时它是安全的,我们将使用ConcurrentQueue <。 / p>

在此队列中,我们会放置TaskCompletionSource个对象。当我们想要开始等待时,它可以创建一个TCS,将其添加到队列中,然后通知信号量异步地弹出队列中的下一个项目并将其标记为&#34;已完成&#34;等待结束时我们知道队列中的项目总会有相同或更少的延续。

然后我们等待来自TCS的Task

我们还可以简单地创建一个返回任务的WaitAsync方法,只需返回它而不是等待它。

public class SemaphoreQueue
{
    private SemaphoreSlim semaphore;
    private ConcurrentQueue<TaskCompletionSource<bool>> queue =
        new ConcurrentQueue<TaskCompletionSource<bool>>();
    public SemaphoreQueue(int initialCount)
    {
        semaphore = new SemaphoreSlim(initialCount);
    }
    public SemaphoreQueue(int initialCount, int maxCount)
    {
        semaphore = new SemaphoreSlim(initialCount, maxCount);
    }
    public void Wait()
    {
        WaitAsync().Wait();
    }
    public Task WaitAsync()
    {
        var tcs = new TaskCompletionSource<bool>();
        queue.Enqueue(tcs);
        semaphore.WaitAsync().ContinueWith(t =>
        {
            TaskCompletionSource<bool> popped;
            if (queue.TryDequeue(out popped))
                popped.SetResult(true);
        });
        return tcs.Task;
    }
    public void Release()
    {
        semaphore.Release();
    }
}

答案 1 :(得分:0)

我已经创建了FifoSemaphore类,并且已经在我的解决方案中成功使用了它。当前的限制是它的行为类似于Semaphore(1,1)。

$q = "SELECT GROUP_CONCAT(DISTINCT
    CONCAT('MAX(CASE WHEN `date` = ''', `date`,
        ''' THEN `close` END) `', `date`, '`')
        ORDER BY YEAR(`date`), MONTH(`date`) 
    )
    INTO @sql
    FROM 
    (SELECT symbol, `date`, `close`
        FROM history where `date` IN 
        (SELECT MAX(`date`) as max_date
            FROM history
            WHERE date BETWEEN '1998-01-01' AND '2018-12-31' AND symbol = 'QQQ'
            GROUP BY YEAR(`date`) )
    ) a;

    SET @sql = CONCAT('SELECT symbol, ', @sql, ' 
    FROM history 
    GROUP BY symbol');

    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;";

$stmt = $this->mysqli->prepare($q);
$stmt->execute();

用法就像使用常规信号灯一样:

public Dictionary<Type, List<object>> FindDbContextsInAssemblies()
{
   var dbContexts = new Dictionary<Type, List<object>>();

   var assemblies = AppDomain.CurrentDomain.GetAssemblies();

   foreach(var assembly in assemblies)
   {
      foreach(var type in assembly.GetTypes())
      {
         if(type.IsSubclassOf(typeof(DbContext)))
         {
            // Instantiate DbContext:
            var context = type.GetConstructor(Array.Empty<Type>()).Invoke(Array.Empty<object>());

            // Find method to get entities:
            var model = type.GetProperty("Model");
            var searchMethod = model.PropertyType.GetMethod("GetEntityTypes");

            // Get registered entities:
            var entities = searchMethod.Invoke(model.GetValue(context, null), null) as List<object>;

            dbContexts[type] = entities;
         }
      }
   }

   return dbContexts;
}

在每个线程上:

public class FifoSemaphore
{
    private readonly object lockObj = new object();

    private List<Semaphore> WaitingQueue = new List<Semaphore>();


    private Semaphore RequestNewSemaphore()
    {
        lock (lockObj)
        {
            Semaphore newSemaphore = new Semaphore(1, 1);
            newSemaphore.WaitOne();
            return newSemaphore;
        }
    }

    #region Public Functions

    public void Release()
    {
        lock (lockObj)
        {
            WaitingQueue.RemoveAt(0);
            if (WaitingQueue.Count > 0)
            {
                WaitingQueue[0].Release();
            }
        }
    }

    public void WaitOne()
    {
        Semaphore semaphore = RequestNewSemaphore();
        lock (lockObj)
        {
            WaitingQueue.Add(semaphore);
            semaphore.Release();

            if(WaitingQueue.Count > 1)
            {
                semaphore.WaitOne();
            }
        }
        semaphore.WaitOne();

    }

    #endregion
}