【问题标题】:BackgroundWorker strange issuesBackgroundWorker 奇怪的问题
【发布时间】:2014-04-21 17:41:29
【问题描述】:

原来 GetUserByID 方法存在问题,然后更新了库,问题似乎已经消失,仍然学习如何更好地访问线程外的 GUI。强>

我使用 TweetInvi 库编写了一个应用程序,它检索用户的关注者和关注者,以及他们的图片、图片链接和 Twitter ID。

然后它遍历返回的列表并显示它们(都在不同的列表中)

现在,当我第一次开始使用这个应用程序时,我已经在 _Click 事件上运行了所有内容,并且当然会冻结 UI 直到它完成。

我现在已将代码移至后台工作线程,这会导致一些奇怪的问题。

有时它会“选择”不填充某些列表,有时它会。 有时它会正确加载所有列表,除了“关注你”列表,它会过滤您的哪些朋友正在关注您(使用 If 语句过滤已验证帐户)

起初我读到尝试在单独的线程上更新 UI 会导致奇怪的错误,因此我删除了除了它填充的列表之外的所有 UI 控件更改。

 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 无法从 UI 线程中卸载工作,我现在很难看到它的使用,抱歉,很长的帖子,感谢阅读。

修复尝试

我在任务运行时禁用了查找按钮,但仍然出现同样的问题。

我还尝试使用 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();

我已经尝试了这两个答案作为解决方案,但仍然会导致不同严重程度的相同问题,我仍然遇到这样的问题,即取消注释代码以检索名称/图片等仍然会阻止后台工作程序运行。不管它是从哪里运行的。

【问题讨论】:

  • 你在哪里打电话给backgroundWorker1.RunWorkerAsync()?可能是您正在创建多个 backgroundWorker 实例?
  • 嗨对不起,我会更新帖子,但它很简单从“查找@User”点击事件中调用,我的鼠标有点错误,它有时会双击但我已经双重检查它不是我几乎每次点击时都知道原因。
  • 感谢您的编辑,有时我在发布到 StackOverflow 时会有点不知所措。
  • 您应该在异步任务运行时禁用按钮btnFind,否则用户将能够再次“运行”(尝试)它,这将导致异常。所以首先我会实现这个行为(运行时禁用,完成时启用 - 你可以在 RunWorkerComplete 事件中做到这一点)。
  • 嗨,Etaiso,我现在就去做并报告:) 更新 - 仍然遇到同样的问题。

标签: c# multithreading backgroundworker tweetinvi


【解决方案1】:

您可能需要在您尝试在后台线程上更新的列表控件上使用 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);
    }

【讨论】:

  • 您好 Vishwaram,感谢您抽出宝贵的时间来回答,我已经尝试过您建议的调用方法,但我不确定我是否理解您对对象锁定的意思,您的意思是实例化用户对象一次并在每次函数查询时锁定它?如果是这样,我需要从保存用户名的文本框中提供一个名称,你不能在全局范围内这样做吗?
  • @Pheonix2105,第二个语句更像是一个通用的多线程注释,以防您希望多个线程访问相同的共享资源。它可能与您要完成的工作没有任何关系。
【解决方案2】:

您违反了 Windows GUI 编程中的一条基本规则:切勿从与创建控件的线程不同的线程访问控件。当您违反此规则时,会发生糟糕的事情;)

通过backgroundWorker1.RunWorkerAsync(boxUsername.Text); 传递用户名值,并通过e.Arguments as string 读取它。

然后您需要使用BeginInvoke 与 UI 控件进行交互。理想情况下,您应该优化此 lambda 以暂停控件的布局,一次调用替换整个项目列表,然后恢复控件的布局。

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

我会使用异步Control.BeginInvoke 而不是同步Control.Invoke 选项。似乎没有理由等待控件呈现您的更改。

【讨论】:

  • 嗨史蒂夫,感谢您的回答,我会尽快尝试并报告,我只是想了解我正在更改的内容,然后再不理解我的代码哈哈。
猜你喜欢
  • 2015-02-27
  • 2018-06-26
  • 2011-08-13
  • 2011-08-17
  • 2011-02-02
  • 2021-10-10
  • 2010-12-30
  • 2014-03-21
  • 2018-07-05
相关资源
最近更新 更多