简体   繁体   English

监视队列的线程<Actions>

[英]Threads monitoring a Queue<Actions>

I doing a small project to map a network (routers only) using SNMP. 我做了一个小项目,使用SNMP映射网络(仅路由器)。 In order to speed things up, I´m trying to have a pool of threads responsible for doing the jobs I need, apart from the first job which is done by the main thread. 为了加快工作速度,我试图让一个线程负责执行我需要的工作,除了第一个工作是由主线程完成的。

At this time I have two jobs, one takes a parameter the other doesn´t: 目前,我有两项工作,一项需要参数,而另一项则不需要:

  • UpdateDeviceInfo(NetworkDevice nd)
  • UpdateLinks() *not defined yet UpdateLinks() *尚未定义

What I´m trying to achieve is to have those working threads waiting for a job to appear on a Queue<Action> and wait while it is empty. 我要实现的目标是让那些工作线程等待作业出现在Queue<Action>然后等待它为空。 The main thread will add the first job and then wait for all workers, which might add more jobs, to finish before starting adding the second job and wake up the sleeping threads. 主线程将添加第一个作业,然后等待所有可能添加更多作业的工作程序完成,然后再开始添加第二个作业并唤醒睡眠线程。

My problem/questions are: 我的问题是:

  • How to define the Queue<Actions> so that I can insert the methods and the parameters if any. 如何定义Queue<Actions>以便我可以插入方法和参数(如果有)。 If not possible I could make all functions accept the same parameter. 如果不可能的话,我可以使所有函数都接受相同的参数。

  • How to launch the working threads indefinitely. 如何无限期启动工作线程。 I not sure where should I create the for(;;) . 我不确定应该在哪里创建for(;;)

This is my code so far: 到目前为止,这是我的代码:

public enum DatabaseState
{
    Empty = 0,
    Learning = 1,
    Updating = 2,
    Stable = 3,
    Exiting = 4
};

public class NetworkDB
{
    public Dictionary<string, NetworkDevice> database;
    private Queue<Action<NetworkDevice>> jobs;
    private string _community;
    private string _ipaddress;

    private Object _statelock = new Object();
    private DatabaseState _state = DatabaseState.Empty;

    private readonly int workers = 4;
    private Object _threadswaitinglock = new Object();
    private int _threadswaiting = 0;

    public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }

    public NetworkDB(string community, string ipaddress)
    {
        _community = community;
        _ipaddress = ipaddress;
        database = new Dictionary<string, NetworkDevice>();
        jobs = new Queue<Action<NetworkDevice>>();
    }

    public void Start()
    {
        NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
        if (nd.Status > NetworkDeviceStatus.Unknown)
        {
            database.Add(nd.Id, nd);
            _state = DatabaseState.Learning;
            nd.Update(this); // The first job is done by the main thread 

            for (int i = 0; i < workers; i++)
            {
                Thread t = new Thread(JobRemove);
                t.Start();
            }

            lock (_statelock)
            {
                if (_state == DatabaseState.Learning)
                {
                    Monitor.Wait(_statelock);
                }
            }

            lock (_statelock)
            {
                if (_state == DatabaseState.Updating)
                {
                    Monitor.Wait(_statelock);
                }
            }

            foreach (KeyValuePair<string, NetworkDevice> n in database)
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
                {
                    file.WriteLine(n);

                }
            }
        }
    }

    public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
    {
        lock (jobs)
        {
            jobs.Enqueue(item);
            if (jobs.Count == 1)
            {
                // wake up any blocked dequeue
                Monitor.Pulse(jobs);
            }
        }
    }

    public void JobRemove()
    {
        Action<NetworkDevice> item;
        lock (jobs)
        {
            while (jobs.Count == 0)
            {
                lock (_threadswaitinglock)
                {
                    _threadswaiting += 1;
                    if (_threadswaiting == workers)
                        Monitor.Pulse(_statelock);
                }
                Monitor.Wait(jobs);
            }

            lock (_threadswaitinglock)
            {
                _threadswaiting -= 1;
            }

            item = jobs.Dequeue();
            item.Invoke();
        }
    }

    public bool NetworkDeviceExists(NetworkDevice nd)
    {
        try
        {
            Monitor.Enter(database);
            if (database.ContainsKey(nd.Id))
            {
                return true;
            }
            else
            {
                database.Add(nd.Id, nd);
                Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
                jobs.Enqueue(action);
                return false;
            }
        }
        finally
        {

            Monitor.Exit(database);
        }
    }

    //Job1 - Learning -> Update device info
    public void UpdateDeviceInfo(NetworkDevice nd)
    {
        nd.Update(this);
        try
        {
            Monitor.Enter(database);
            nd.Status = NetworkDeviceStatus.Self;
        }
        finally
        {
            Monitor.Exit(database);
        }
    }

    //Job2 - Updating -> After Learning, create links between neighbours
    private void UpdateLinks()
    {

    }
}

Your best bet seems like using a BlockingCollection instead of the Queue class. 最好的选择似乎是使用BlockingCollection而不是Queue类。 They behave effectively the same in terms of FIFO, but a BlockingCollection will let each of your threads block until an item can be taken by calling GetConsumingEnumerable or Take. 它们在FIFO方面的行为实际上是相同的,但是BlockingCollection将允许您的每个线程都阻塞,直到可以通过调用GetConsumingEnumerable或Take来获取某项为止。 Here is a complete example. 这是一个完整的例子。

http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1 http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1

As for including the parameters, it seems like you could use closure to enclose the NetworkDevice itself and then just enqueue Action instead of Action<> 至于包含参数,似乎您可以使用闭包将NetworkDevice自身封装起来,然后仅使Action入队而不是Action <>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM