Многопоточное консольное приложение С# для сбора данных с сайтов

Я написал приложение, которое просматривает наши собственные свойства и удаляет данные. Чтобы убедиться, что я не использую одни и те же URL-адреса, я использую базу данных MySQL для хранения URL-адреса, отмечаю его после обработки. Все это делалось в одном потоке, и ничего страшного, если бы у меня было всего несколько тысяч записей. Но у меня есть несколько сотен тысяч записей, которые мне нужно проанализировать, поэтому мне нужно внести изменения в код (я вообще новичок в многопоточности). Я нашел пример и пытался скопировать стиль, но, похоже, не работает. Кто-нибудь знает, в чем проблема со следующим кодом?

РЕДАКТИРОВАТЬ: Извините, я не хотел, чтобы люди догадывались о проблеме, но с моей стороны было глупо включать исключение. Вот исключение "System.InValidCastException: 'Указанное приведение недопустимо'". Когда я запускаю процесс, он собирает URL-адреса из базы данных, а затем никогда не обращается к методу DoWork.

//Это позволит получить записи из базы данных

List<Mappings> items = bot.GetUrlsToProcess(100);
if (items != null)
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;
    Worker.Done = new Worker.DoneDelegate(WorkerDone);
    foreach (var item in items)
    {
        urls.Add(item.Url);
        WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token));
     }
     LaunchTasks();

 }


static async void LaunchTasks()
{
        // keep checking until we're done
        while ((WaitingTasks.Count > 0) || (RunningTasks.Count > 0))
        {
            // launch tasks when there's room
            while ((WaitingTasks.Count > 0) && (RunningTasks.Count < MaxRunningTasks))
            {
                Task task = WaitingTasks.Dequeue();
                lock (RunningTasks) RunningTasks.Add((int)task.AsyncState, task);
                task.Start();
            }
            UpdateConsole();
            await Task.Delay(300); // wait before checking again
        }
        UpdateConsole();    // all done
 }


 static void UpdateConsole()
 {
        Console.Write(string.Format("\rwaiting: {0,3:##0}  running: {1,3:##0} ", WaitingTasks.Count, RunningTasks.Count));
 }

 static void WorkerDone(int id)
 {
        lock (RunningTasks) RunningTasks.Remove(id);
 }


 public class Worker
 {
    public delegate void DoneDelegate(int taskId);
    public static DoneDelegate Done { private get; set; }

    public async void DoWork(object id, string url, CancellationToken token)
    {
        if (token.IsCancellationRequested) return;
        Content obj;
        try
        {
            int tries = 0;
            bool IsUrlProcessed = true;

            DateTime dtStart = DateTime.Now;
            string articleDate = string.Empty;

            try
            {
                ScrapeWeb bot = new ScrapeWeb();

                SearchApi searchApi = new SearchApi();
                SearchHits searchHits = searchApi.Url(url, 5, 0);
                if (searchHits.Hits.Count() == 0)
                {
                    obj = await bot.ReturnArticleObject(url);
                    if (obj.Code != HttpStatusCode.OK)
                    {
                        Console.WriteLine(string.Format("\r Status is {0}", obj.Code));
                        tries = itemfound.UrlMaxTries + 1;
                        IsUrlProcessed = false;
                        itemfound.HttpCode = obj.Code;
                    }
                    else
                    {

                        string title = obj.Title;
                        string content = obj.Contents;
                        string description = obj.Description;

                        Articles article = new Articles();
                        article.Site = url.GetSite();
                        article.Content = content;
                        article.Title = title;
                        article.Url = url.ToLower();
                        article.Description = description;
                        string strThumbNail = HtmlHelper.GetImageUrl(url, obj.RawResponse);
                        article.Author = HtmlHelper.GetAuthor(url, obj.RawResponse);
                        if (!string.IsNullOrEmpty(strThumbNail))
                        {
                            //This condition needs to be added to remove ?n=<number> from EP thumbnails
                            if (strThumbNail.Contains("?"))
                            {
                                article.ImageUrl = strThumbNail.Substring(0, strThumbNail.IndexOf("?")).Replace("http:", "https:");
                            }
                            else
                                article.ImageUrl = strThumbNail.Replace("http:", "https:");
                        }
                        else
                        {
                            article.ImageUrl = string.IsNullOrEmpty(strThumbNail) ? article.Url.GetDefaultImageUrls() : strThumbNail.Replace("http:", "https:");
                        }

                        articleDate = HtmlHelper.GetPublishDate(url, obj.RawResponse);
                        if (string.IsNullOrEmpty(articleDate))
                            article.Pubdate = DateTime.Now;
                        else
                            article.Pubdate = DateTime.Parse(articleDate);


                        var client = new Index(searchApi);
                        var result = client.Upsert(article);
                        itemfound.HttpCode = obj.Code;
                        if (result)
                        {
                            itemfound.DateCreated = DateTime.Parse(articleDate);
                            itemfound.DateModified = DateTime.Parse(articleDate);
                            UpdateItem(itemfound);
                        }
                        else
                        {
                            tries = itemfound.UrlMaxTries + 1;
                            IsUrlProcessed = false;
                            itemfound.DateCreated = DateTime.Parse(articleDate);
                            itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
                            UpdateItem(itemfound, tries, IsUrlProcessed);
                        }
                    }
                }
                else
                {
                    tries = itemfound.UrlMaxTries + 1;
                    IsUrlProcessed = true;
                    itemfound.HttpCode = HttpStatusCode.OK;
                    itemfound.DateCreated = DateTime.Parse(articleDate);
                    itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);
                }
            }
            catch (Exception e)
            {
                tries = itemfound.UrlMaxTries + 1;
                IsUrlProcessed = false;
                itemfound.DateCreated = DateTime.Parse(articleDate);
                itemfound.DateModified = DateTime.Parse(articleDate) == null ? DateTime.Now : DateTime.Parse(articleDate);

            }
            finally
            {
                DateTime dtEnd = DateTime.Now;

                Console.WriteLine(string.Format("\r Total time taken to process items is {0}", (dtEnd - dtStart).TotalSeconds));

            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }




        Done((int)id);
    }

}

Весь этот код основан на лучшем многопоточном подходе для нескольких веб-запросов эта ссылка. Может ли кто-нибудь сказать мне, как запустить этот подход?


person Subrato M    schedule 30.03.2018    source источник
comment
Не спрашивайте нас, в чем проблема, вы говорите нам о проблеме. У нас нет базы данных из 100 000 URL-адресов, чтобы запустить это, и это много кода, чтобы просмотреть и попытаться угадать проблему. Пожалуйста, расскажите нам, что вы хотите, чтобы произошло и что происходит на самом деле. Если есть ошибка, сообщите нам, какую строку и какую именно ошибку вы получаете.   -  person Ron Beyer    schedule 30.03.2018
comment
не работает - недостаточное описание проблемы.   -  person hatchet - done with SOverflow    schedule 30.03.2018
comment
Я получаю исключение System.InValidCastException: «Указанное приведение недопустимо». Когда я запускаю процесс, он собирает URL-адреса из базы данных, а затем никогда не использует метод DoWork.   -  person Subrato M    schedule 30.03.2018
comment
@SubratoM Пожалуйста, сообщите нам, в какой строке возникает это исключение, мы не можем догадаться.   -  person Ron Beyer    schedule 30.03.2018


Ответы (2)


Я думаю, проблема в том, как вы создаете свои задачи:

new Task(id => new Worker().DoWork((int)id, item.Url, token), item.Url, token)

Эта перегрузка конструктора Task ожидала Action<object> делегата. Это означает, что id будет напечатано как object, и вам нужно сначала привести его к чему-то полезному.

Параметры

action

  • Тип: System.Action<Object>
  • Делегат, представляющий код для выполнения в задаче.

state

  • Тип: System.Object
  • Объект, представляющий данные, которые будут использоваться действием.

cancellationToken

  • Введите: System.Threading.CancellationToken - CancellationToken, за которым будет наблюдать новая задача.

Вы решили привести его к int, вызвав (int)id, но вы передаете item.Url как сам объект. Я не могу сказать вам на 100%, что такое тип Url, но я не ожидаю, что свойство с именем Url будет иметь тип int.

person MarcinJuraszek    schedule 30.03.2018
comment
как мне передать строку в DoWork? Я заметил исключения в этой строке RunningTasks.Add((int)task.AsyncState, task); Метод добавления принимает только то, что у меня нет идентификатора, чтобы передать ему. URL-адрес — это в основном URL-адрес, который я хочу отсканировать, а затем обработать возвращаемые данные. - person Subrato M; 31.03.2018

Основываясь на том, что сказал @MarcinJuraszek, я просто вернулся к своему коду и добавил int, так как не смог найти другого способа его решить. Вот изменение, которое я сделал

int i=0
foreach (var item in items)
{
    urls.Add(item.Url);
    WaitingTasks.Enqueue(new Task(id => new Worker().DoWork((string)id, item.Url, token), item.Url, token));
    i++;
 }
person Subrato M    schedule 31.03.2018