private void CollectLinksToObjects(Object o)
        {
            //const int dublicateCountObjsToRecheck = 20;
            //string[] dublicateLastAddedLinks = new string[dublicateCountObjsToRecheck];
            //int dublicateLoopCounter = 0;
            //bool[] dublicateMatrix = new bool[dublicateCountObjsToRecheck];
            Instruction instruction = null;
            NetFetcher net = new NetFetcher();
            bool stopLoop = false;
            Template template = o as Template;
            if (template == null)
                return;
            HtmlParser parser = new HtmlParser();
            int countLinksObtained = -1;

            int countInstructions = 0;

            SortedList<int, bool> existingKeys = new SortedList<int, bool>();//список добавленных ключей 

            do
            {

                if (countLinksObtained == 0)//если не нашли ниодной ссылки- сбрасываем текущие  инструкции
                    instruction = null;

                //if (dublicateLoopCounter >= dublicateCountObjsToRecheck - 1)
                //{
                //    instruction = null;
                //    dublicateLoopCounter = 0;
                //}

                if (instruction == null || string.IsNullOrEmpty(instruction.link.Trim()))
                {
                    //instruction = null;
                    lock (instructionLocker)
                    {
                        if (instructions.Count() == 0)
                        {
                            Console.WriteLine("All instructions were processed, CollectLinksToObjects thread going to terminate");
                            eventLog.AddEventToLog("CollectLinksToObjects Инструкции закончились,  завершаем работу");
                            break;
                        }
                        else
                        {
                            instruction = instructions.Dequeue();//берем инструкцию с очереди
                            countInstructions = instructions.Count();
                            countLinksObtained = -1;
                        }
                    }
                }

                eventLog.AddEventToLog("CollectLinksToObjects количество инструкций в очереди: " + countInstructions.ToString());

                string content = string.Empty;
                if (!stopLoop && instruction != null)
                {
                    content = net.DownloadWebPage(instruction.link, Encoding.UTF8);//скачиваем страницу по линку из инструкций

                    countLinksObtained = 0;//сбрасываем счетчик полученных линков
                    foreach (var item in parser.GetLinks(content, instruction.host, template.links[0]))//получаем линки на объявления
                    {
                        if (string.IsNullOrEmpty(item)) continue;

                        bool canAddToQueue = true;
                        int currentKey = SearchIdFromURL(item.ToLower(), "ad_id");
                        if (existingKeys.ContainsKey(currentKey))
                        {
                            canAddToQueue = false;
                            eventLog.AddEventToLog("Найден дубликат в ключах url для " + currentKey.ToString());
                        }
                        else
                            existingKeys.Add(currentKey, true);
                        //if(existingKeys.ContainsKey(
                        //for (int i = 0; i < dublicateCountObjsToRecheck; i++)
                        //    if (dublicateLastAddedLinks[i] == item)
                        //    {
                        //        dublicateMatrix[i] = true;//dublicate found
                        //        canAddToQueue = false;//can't add current link to global queue
                        //        dublicateLoopCounter++;//считаем количество дубликатов
                        //        break;
                        //    }

                        if (canAddToQueue)//in case no dublication for last elements
                        {
                            //if (dublicateLoopCounter >= dublicateCountObjsToRecheck - 1)
                            //    dublicateLoopCounter = 0;

                            //dublicateLastAddedLinks[dublicateLoopCounter++] = item;


                            int countLinksToObject = -1;

                            Instruction linkToObj = new Instruction()
                            {
                                city = instruction.city,
                                district = instruction.district,
                                host = instruction.host,
                                link = item,
                                propertyAction = instruction.propertyAction,
                                propertyType = instruction.propertyType
                            };
                            lock (linksToObjectLocker)
                            {
                                linksToObjects.Enqueue(linkToObj);//adding url to object in global queue                           
                                countLinksToObject = linksToObjects.Count;
                            }
                            Console.WriteLine("item: " + item);
                            countLinksObtained++;

                            linkToObj = null;


                            lock (collectEntityLock)//разлочиваем сборку объектов
                            {
                                Monitor.PulseAll(collectEntityLock);
                            }

                            if (countLinksToObject >= LimitList)//если превышен лимит ссылок на объеты -лочимся
                            {
                                //lock (collectEntityLock)//разлочиваем сборку объектов
                                //{
                                //    Monitor.PulseAll(collectEntityLock);
                                //}

                                lock (linksToObjectLimitLocker)
                                {
                                    Monitor.Wait(linksToObjectLimitLocker);
                                }
                            }


                        }
                    }

                    string currentLink = instruction.link;

                    if (template.pages[0].Count() > 1)
                    {
                        string tmpData = string.Empty;
                        for (int i = 0; i < template.pages.Count - 1; i++)
                        {
                            if (string.IsNullOrEmpty(tmpData))
                                tmpData = parser.GetSingleContent(content, template.pages[i]);
                            else
                                tmpData = parser.GetSingleContent(tmpData, template.pages[i]);

                        }


                        foreach (var item in parser.GetLinks(tmpData, instruction.host, template.pages[template.pages.Count() - 1]))
                        {
                            instruction.link = item;
                            break;
                        }


                    }
                    else
                    {
                        foreach (var item in parser.GetLinks(content, instruction.host, template.pages[0]))
                        {
                            instruction.link = item;
                            break;
                        }
                    }



                    if (currentLink == instruction.link && countInstructions == 0)//если предыдущая ссылка и новая  совпадают- достигли конца
                        stopLoop = true;

                    if (countLinksObtained == 0 && countInstructions == 0)//если предыдущая ссылка и новая  совпадают- достигли конца
                        stopLoop = true;

                }

                //if (string.IsNullOrEmpty(instruction.link) || stopLoop)
                //{
                //    stopLoop = false;
                //    lock (instructionLocker)
                //    {
                //        if (instructions.Count > 0)
                //            instruction = instructions.Dequeue();
                //        else stopLoop = true;
                //    }
                //}

            } while (!stopLoop);


            lock (collectEntityLock)//разлочиваем ожидающий поток на прощание
            {
                Monitor.PulseAll(collectEntityLock);
            }


            Console.WriteLine("CollectLinksToObjects terminated");
            return;

        }
        private void CollectEntities(Object o)
        {
            lock (collectEntityLock)//лочим текущий поток чтобы не обгонять поток получения ссылок на объекты
            {
                Monitor.Wait(collectEntityLock);
            }

            NetFetcher net = new NetFetcher();
            bool stopLoop = false;
            Template template = o as Template;
            if (template == null)
                return;
            HtmlParser parser = new HtmlParser();
            DataExtactor extractor = new DataExtactor();
            Instruction currentUrl = null;

            var sha = new System.Security.Cryptography.SHA256Managed();
            var uEncode = new UnicodeEncoding();
            List<ulong> h1 = null;

            int countLinksToObject = 0;

            try
            {
                do
                {
                    lock (linksToObjectLocker)
                    {
                        if (linksToObjects.Count > 0)
                            currentUrl = linksToObjects.Dequeue();//пытаемся получить ссылку на объект
                        countLinksToObject = linksToObjects.Count();
                    }

                    if (countLinksToObject <= minLimitList)
                    {
                        lock (linksToObjectLimitLocker)
                        {
                            Monitor.PulseAll(linksToObjectLimitLocker);
                        }
                    }

                    if (currentUrl == null)//если ссылку получить не удалось
                    {
                        if (!t2.IsAlive)//пришло время останавливаться
                        {
                            eventLog.AddEventToLog("CollectEntities- Нет больше ссылок на объекты,  поток собиратель ссылок- завршился- завершаем работу и мы");
                            break;//останавливаем главный цикл
                        }
                        else//поток собирающий ссылки еще живой
                        {
                            lock (linksToObjectLimitLocker)
                            {
                                Monitor.PulseAll(linksToObjectLimitLocker);
                            }


                            //eventLog.AddEventToLog("CollectEntities- Нет ссылок на объекты,  поток собиратель еще живой- зысыпаем");

                            lock (collectEntityLock)//останавливаемся и ждем когда нас опять позовут
                            {
                                Monitor.Wait(collectEntityLock);
                            }
                            continue;//начинаем  с начала цикла
                        }
                    }
                    else//если ссылку получили
                    {
                        if (string.IsNullOrEmpty(currentUrl.link))//если урл ссылка пустая
                        {
                            eventLog.AddEventToLog("CollectEntities- ОШИБКА - пришла пустая ссылка");
                            continue;// начинаем цикл по новой
                        }

                        //если добрались сюда- значит ссылку получили и  можем с ней работать

                        #region проверяем линк на дубликаты
                        bool dublicateLink = false;
                        currentUrl.link = currentUrl.link.ToLower();
                        lock (processedLinksLocker)
                        {
                            if (processedLinks.ContainsKey(currentUrl.link))
                            {
                                dublicateLink = true;
                                currentUrl = null;//обнуляем текущую  ссылку т.к. найден дубликат
                                Console.WriteLine("dublicat found");
                            }
                        }
                        if (dublicateLink)//если нашли дубликат- начнаем с начала  цикла
                            continue;
                        else//если дубликата не найден- добавлеям ссылку в список обработанных
                        {
                            lock (processedLinksLocker)
                            {
                                processedLinks.Add(currentUrl.link, true);
                                linkLogger.AddLinkToLog(currentUrl.link);
                            }
                        }
                        #endregion

                        string content = net.DownloadWebPage(currentUrl.link, Encoding.UTF8);//скачиваем страницу
                        Template tmpTemplate = Template.DeepClone<Template>(template);//делаем дубликат шаблона

                        Entity ent = extractor.Extract(content, template.HOST, tmpTemplate);//template);//извлекаем объект со страницы

                        if (ent != null)//если объект найден
                        {

                            #region проверяем текст объявления на дубликаты

                            string clearContent = parser.ClearContent(ent.description);

                            if (string.IsNullOrEmpty(clearContent))//если текст  объявления после очистки пустой- пропускаем такое объявление
                            {
                                eventLog.AddEventToLog("ВНИМАНИЕ!!!Пустое объявление по ссылке " + ent.linkToOriginalObject);
                                continue;
                            }

                            h1 = hash.ComputeHash(clearContent);//считаем хеши
                            if (h1 == null)
                            {
                                eventLog.AddEventToLog("ВНИМАНИЕ!!!Ошибка получения хеша для  " + clearContent);
                                continue;
                            }
                            float percentage = hash.IsExist(h1);//проверяем на наличие похожих хешей
                            if (percentage < 90.0)
                            {
                                hash.AddNewHash(h1);//добавляем если предел не превышен
                            }
                            else// найден дублика объявления
                            {
                                Console.WriteLine("Text Dublicate found");
                                ent = null;
                                tmpTemplate = null;
                                content = null;
                                currentUrl = null;
                                continue;
                            }
                            #endregion

                            ent.linkToOriginalObject = currentUrl.link;//сохраняем линк на оригинал объявления

                            if (string.IsNullOrEmpty(ent.phone1))//если  телефон1 не заполнен еще- пытаемся его получить
                            {
                                //пытаемся получить телефоны
                                foreach (var item in parser.GetLinks(content, template.HOST, template.phone[0]))
                                {
                                    content = net.DownloadWebPage(item, Encoding.UTF8);//скачиваем указанную страницу для получения телефона
                                    var newPhoneTemplate = new string[1];
                                    newPhoneTemplate[0] = template.phone[1];
                                    extractor.ExtractPhone(content, ent, newPhoneTemplate);
                                    break;
                                }
                            }

                            #region получаем фотографии
                            if (ent.LinkToPhotos != null)
                            {
                                ent.photos = new List<byte[]>();
                                for (int i = 0; i < ent.LinkToPhotos.Count; i++)
                                {
                                    if (!string.IsNullOrEmpty(ent.LinkToPhotos[i]))
                                        ent.photos.Add(
                                            net.getDataAsByteArray(ent.LinkToPhotos[i])
                                            );
                                }
                            }
                            #endregion

                            //дописываем вспомогательную информацию
                            ent.propertyType = currentUrl.propertyType;
                            ent.propertyAction = currentUrl.propertyAction;
                            ent.City = currentUrl.city;
                            ent.District = currentUrl.district;

                            int countEntities = 0;
                            lock (entitiesLocker)//добавляем полученый объект в очередь на сохранение
                            {
                                entities.Enqueue(ent);
                                countEntities = entities.Count();
                            }
                            ent = null;


                            lock (dbLoaderLock)
                            {
                                Monitor.PulseAll(dbLoaderLock);
                            }

                            if (countEntities > LimitList)//если  превысили лимит  объектов в списке лочим текущий поток
                            {


                                lock (collectEntityLock)
                                {
                                    Monitor.Wait(collectEntityLock);
                                }
                            }
                        }
                        else
                        {
                            eventLog.AddEventToLog("ОШИБКА объект по сслыке не найден: " + currentUrl.link);
                        }
                    }


                    if (!t1.IsAlive && countLinksToObject == 0)
                        stopLoop = true;




                } while (!stopLoop);//пока все условия соблюдаются
            }
            catch (Exception ex)
            {
                eventLog.AddEventToLog("ИСКЛЮЧЕНИЕ!!! CollectEntities " + ex.Message);
                hash.StoreCache();//сохряняем хеши
            }
            finally
            {

                lock (dbLoaderLock)//разлочиваем поток загрузки объектов в БД на прощание
                {
                    Monitor.PulseAll(dbLoaderLock);
                }

                Console.WriteLine("CollectEntities terminated");
            }
            return;

        }