Concurrency-safe task scheduler I'm trying to do some task scheduler and worker. Tasks are added to the queue from which worker retrieves and processes them. Tasks from one queue must be executed sequentially. Tasks from different queues are executed in parallel. When the worker has completed all the tasks in the queue, he waits for a while and finishes his work. The queue must be removed from the schedule

I did something, but I think it isn't the best solution. I think there may be problems when worker shuts down and closes the channel.

Is my solution concurrency-safe?

// Schedule struct
type Schedule struct {
    sync.RWMutex
    queues map[int]chan Task
    idle   byte
}

// Scheduler pushes task to the queue
func (s *Schedule) Scheduler(t Task, i int) {
    var queue chan Task
    var ok bool

    s.RLock()
    if queue, ok = s.queues[i]; !ok {
        s.RUnlock()
        s.Lock()
        if queue, ok = s.queues[i]; !ok {
            queue = make(chan Task)
            s.queues[i] = queue
            go s.worker(queue, i)
        }
        s.Unlock()
    } else {
        s.RUnlock()
    }

    queue &lt;- t
}

// Worker retrieves task from the queue and process
func (s *Schedule) worker(c chan Task, i int) {
    timeout := time.After(s.idle * time.Second)
    done := false
    for !done {
        select {
        case task := &lt;-c:
            task.Execute()
            timeout = time.After(s.idle * time.Second)

        case &lt;-timeout:
            s.Lock()
            close(c)
            delete(s.queues, i)
            s.Unlock()
            done = true

        default:
            time.Sleep(10 * time.Millisecond)
        }
    }
}
There is no need for the default clause in Schedule.worker.
Memory side, it is better to create a time.Timer object and call Timer.Reset &amp; Timer.Stop on it.
No need for the done variable, we can just return when we are done.


func (s *Schedule) worker(c chan Task, i int) {
    t := time.NewTimer(s.idle * time.Second)
    for {
        select {
        case task := &lt;-c:
            task.Execute()
            if !t.Stop() {
                &lt;-t.C
            }
            t.Reset(s.idle * time.Second)
        case &lt;-t.C:
            s.Lock()
            close(c)
            delete(s.queues, i)
            s.Unlock()
            return
         }
    }
}



It is not advisable to close a channel from a receiver. Better to close the channel when you know that there are no more tasks and have the worker wait for that.


func (s *Schedule) worker(c chan Task, i int) {
    for task := range c {
        task.Execute()
    }
}