BackgroundWorker странные проблемы

Оказалось, что была проблема с методом GetUserByID, затем библиотека была обновлена, и проблема, кажется, ушла, все еще узнав, как лучше получить доступ к GUI вне потока.

Я написал приложение, используя библиотеку TweetInvi, оно извлекает пользователей, подписчиков и подписчиков, а также их изображения, ссылку на картинку и твиттер-идентификатор.

Затем он перебирает возвращенные списки и отображает их (все в разных списках)

Теперь, когда я впервые начал работать с этим приложением, у меня было все запущено для события _Click, и, конечно же, я заморозил интерфейс до его завершения.

Теперь я переместил код в поток фонового работника, и это вызывает некоторые странные проблемы.

Иногда он "выбирает" не заполнять определенные списки, в других случаях это будет. Иногда он загружает все списки правильно, за исключением списка Подписчиков, который фильтрует, кто из ваших друзей следует за вами (с помощью оператора If, чтобы отфильтровать проверенные аккаунты).

Сначала я прочитал, что попытка обновить пользовательский интерфейс в отдельном потоке может вызвать странные ошибки, поэтому я удалил все изменения элемента управления пользовательского интерфейса, за исключением списков, которые он заполняет.

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {   
            BackgroundWorker worker = sender as BackgroundWorker;


        //user name retrieved from text box, rest of this method will pull various bits of data back

        var username = e.Argument.ToString();
        var user = User.GetUserFromScreenName(username);



        Properties.Settings.Default.LastHandle = boxUsername.Text;
        Properties.Settings.Default.Save();



        var usersTweets = user.GetUserTimeline(Convert.ToInt32(txtTweetAmount.Text)).ToList();

        foreach (var userTweet in usersTweets)
        {
            lstSearchTweetList.Invoke((MethodInvoker)delegate
            {
                var searchList = lstSearchTweetList.Items.Add(userTweet.Text);
                  searchList.SubItems.Add(userTweet.CreatedAt.ToString());
            });
        }


        var show = user.GetFollowers(500).ToList();


        foreach (var friend in show)
        {

            string screenName = "@" + friend.ScreenName;
            lstFriend.BeginInvoke((MethodInvoker)delegate
            {
                lstFriend.Items.Add(screenName); // runs on UI thread
            });

        }

        var friends = user.GetFriends(500);
        var followers = user.GetFollowers(500);


        var result2 = followers.Where(follower => friends.All(friend => follower.Name != friend.Name));
        int i2 = 0;
        foreach (var res2 in result2)
        {
            string screenName = "@" + res2.ScreenName;
            lstFollowingChecker.BeginInvoke((MethodInvoker)delegate
                {
                    lstFollowingChecker.Items.Add(screenName);
                });
                i2++;
             //   lblFollowBackAmount.Text = Convert.ToString(i2);
        }
        var result = friends.Where(friend => followers.All(follower => friend.Name != follower.Name));

        //lblFriendCount.Text = "(" + result.Count().ToString() + ")";
        int i1 = 0;
        foreach (var res in result)
        {
                if (res.Verified != true)
                {
                    string screenName = "@" + res.ScreenName;
                    lstFollowerChecker.BeginInvoke((MethodInvoker)delegate
                    {
                        lstFollowerChecker.Items.Add(screenName);
                    });

                    i1++;
                   // lblCheckerCount.Text = Convert.ToString(i1);
                }
        }



        backgroundWorker1.ReportProgress(1,username);
    }

Функция, вызывающая RunWorkerAsync()

private void btnFind_Click(object sender, EventArgs e)
    {

        //start backgroundworker and clear friends and search lists
        pctProgressBar.Visible = true;

        lstFriend.Items.Clear();
        lstSearchTweetList.Items.Clear();
        lstFollowerChecker.Items.Clear();
        lstFollowingChecker.Items.Clear();
        lstFriend.Items.Clear();
        lstSearchTweetList.Items.Clear();

        if (txtTweetAmount.Text == "")
        {
            txtTweetAmount.Text = "20";
        }

        backgroundWorker1.RunWorkerAsync();

    }

Моя проблема в том, что странные необъяснимые ошибки все еще происходят, казалось бы, случайно.

Если это вызвано тем, что списки обновляются в фоновом рабочем потоке, какой смысл использовать фоновый рабочий, если я не могу использовать его для интенсивной работы?

Я также включу две фотографии учетной записи друзей, так как она лучше демонстрирует проблему, поэтому что-то обрабатывает и т. Д. Будет скрыто. Первая проблема заключается в том, что он иногда заполняет список несколько раз, и "Не следит за тобой назад" должен возвращать @Theilluminati только один раз. Первый же экран

Снова это возвращает @Theilluminati, но перечисляет это дважды.Тот же экран во второй раз

Существует также проблема, если я запускаю приведенный ниже код в любом месте, фоновый работник не запускается, то есть он будет вытягивать изображение / имя / местоположение, но фоновый работник не запускается, и если я пытаюсь сделать это в фактическом Фоновый поток, тогда списки не будут заполняться.

   var username = boxUsername.Text;
    var user = User.GetUserFromScreenName(username);
    //string ImageURL = user.ProfileImageUrl;
    //string biggerImageURL = ImageURL.Replace("_normal", "");
    //txtImageURL.Text = biggerImageURL;
    //pctDisplaypicture.ImageLocation = biggerImageURL;
    //txtTwitterID.Text = user.Id.ToString();
    //lblFriendCount.Text = "(" + user.FollowersCount + ")";

Буду признателен за любую помощь, я сейчас изо всех сил пытаюсь увидеть использование Backgroundworker, если он не может выгрузить работу из потока пользовательского интерфейса, извините за длинный пост, спасибо за чтение.

Исправить попытки

Я отключил кнопку поиска во время выполнения задачи, и та же проблема все еще возникает.

Я также пытался использовать if(working.Cancellationpending == true), чтобы разорвать циклы, как только задача была выполнена один раз.

Я изменил циклы foreach по списку на приведенные ниже, соответственно, и передал имя пользователя как переменную вместо того, чтобы извлекать его из элемента управления, проблемы, похоже, только усугубились, списки вообще не заполняются сейчас.

lstSearchTweetList.Invoke((MethodInvoker)delegate
                {
                    lstSearchTweetList.Items.Add(userTweet.Text).SubItems.Add(userTweet.CreatedAt.ToString());
                });

 backgroundWorker1.RunWorkerAsync(boxUsername.Text);

var username = e.Argument.ToString();

Я испробовал оба ответа в качестве решения, и оба они по-прежнему приводят к одной и той же проблеме с разной степенью серьезности. Я все еще застрял в проблеме, заключающейся в том, что раскомментирование кода для получения имени / изображения и т. Д. Все еще блокирует запуск фонового работника. Неважно, откуда он бежит.

2 ответа

Решение

Вам может понадобиться использовать метод Invoke в элементах управления списком, которые вы пытаетесь обновить в фоновом потоке, например:

    string  screenName = "@" + friend.ScreenName;
    lstFriend.Invoke((MethodInvoker)delegate {
           lstFriend.Items.Add(screenName); // runs on UI thread
    });

Одна проблема, которую вы можете иметь с многопоточностью, - это когда вы пытаетесь получить доступ к общим ресурсам (коллекциям, файлам и т. Д.) Из нескольких потоков, может возникнуть взаимоблокировка, а также условия гонки. Чтобы сделать это безопасно, в этом случае будет создан блокирующий объект, который заблокирует код, обращающийся к общему ресурсу. Таким образом, ресурс может быть доступен только по одному за раз.

    //defined globally
    object _MyLockingObject = new object();

и в пределах определенного метода, блокирующего список:

    lock(_MyLockingObject)
    {
       myList.Add(item);
    }

Вы нарушаете фундаментальное правило в программировании Windows GUI: никогда не обращайтесь к элементу управления из потока, который не является тем же потоком, который создал элемент управления. Когда вы нарушаете это правило, случаются плохие дела

Передайте значение имени пользователя через backgroundWorker1.RunWorkerAsync(boxUsername.Text);и читать через e.Arguments as string,

Затем вам нужно использовать BeginInvoke для взаимодействия с элементами управления пользовательского интерфейса. В идеале, вы должны оптимизировать эту лямбду, чтобы приостановить макет элемента управления, заменить весь список элементов одним вызовом и возобновить макет элемента управления.

 // execute on the UI thread
 this.BeginInvoke((Action)(() =>
 {
   lstFriend.Items.Add("@" + friend.ScreenName);
 }), null);

Я бы использовал асинхронный Control.BeginInvoke за синхронизацию Control.Invoke вариант. Похоже, что нет причины ждать на элементе управления для отображения изменений.

Другие вопросы по тегам