public static void EnqueueTask(object state)
    {
        CancellationToken token = (CancellationToken)state;

        using (DSTeckWebPushNotificationsContext db = new DSTeckWebPushNotificationsContext())
        {
            while (!token.IsCancellationRequested)
            {
                if (enqueueSignal.WaitOne())
                {
                    int toEnqueue = 100 - deliveryQueue.Count;

                    if (toEnqueue > 0)
                    {
                        // fetch some records from db to be enqueued
                        NotificationDelivery[] deliveries = db.NotificationDeliveries
                                                            .Include("Subscription")
                                                            .Include("Notification")
                                                            .Include("Notification.NotificationLanguages")
                                                            .Include("Notification.NotificationLanguages.Language")
                                                            .Where(nd => nd.Status == NotificationDeliveryStatus.Pending && DateTime.Now >= nd.StartSendingAt)
                                                            .OrderBy(nd => nd.StartSendingAt)
                                                            .Take(toEnqueue)
                                                            .ToArray();

                        foreach (NotificationDelivery delivery in deliveries)
                        {
                            delivery.Status = NotificationDeliveryStatus.Queued;
                            deliveryQueue.Enqueue(delivery);
                        }

                        if (deliveries.Length > 0)
                        {
                            // save Queued state, so not fetched again the next loop
                            db.SaveChanges();

                            // signal the DequeueTask
                            dequeueSignal.Set();
                        }
                        else
                        {
                            // no more notifications, wait 5 seconds before try fetching again
                            enqueueSignal.Reset();
                            enqueueTimer.Start();
                        }
                    }

                    // save any changes made by the DequeueTask
                    // an event may be used here to know if any changes made
                    db.SaveChanges();
                }
            }

            Task.WaitAll(dequeueTasks);
            db.SaveChanges();
        }
    }
    public static void StartSending(CancellationToken token)
    {
        PushService.InitServices();

        using (DSTeckWebPushNotificationsContext db = new DSTeckWebPushNotificationsContext())
        {
            NotificationDelivery[] queuedDeliveries = db.NotificationDeliveries
                                                      .Where(nd => nd.Status == NotificationDeliveryStatus.Queued)
                                                      .ToArray();

            foreach (NotificationDelivery delivery in queuedDeliveries)
            {
                delivery.Status = NotificationDeliveryStatus.Pending;
            }

            db.SaveChanges();
        }

        enqueueSignal = new ManualResetEvent(true);
        dequeueSignal = new ManualResetEvent(false);

        enqueueTimer           = new System.Timers.Timer();
        enqueueTimer.Elapsed  += EnqueueTimerCallback;
        enqueueTimer.Interval  = 5000;
        enqueueTimer.AutoReset = false;
        enqueueTimer.Stop();

        enqueueTask = new Task(EnqueueTask, token, TaskCreationOptions.LongRunning);
        enqueueTask.Start();

        deliveryQueue = new ConcurrentQueue <NotificationDelivery>();

        int dequeueTasksCount = 10;

        dequeueTasks = new Task[dequeueTasksCount];
        for (int i = 0; i < dequeueTasksCount; i++)
        {
            dequeueTasks[i] = new Task(DequeueTask, token, TaskCreationOptions.LongRunning);
            dequeueTasks[i].Start();
        }
    }