BackgroundWorker奇怪的问题

时间:2014-03-16 15:23:30

标签: c# multithreading backgroundworker tweetinvi

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

我使用TweetInvi库编写了一个应用程序,它检索用户关注者和关注者,还有他们的图片,图片和推特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一次 Same Screen first Time

它再次返回@Theilluminati但列出了两次。 Same Screen Second time

还有一个问题,如果我在任何地方运行以下代码,后台工作程序不会运行,也就是说,它会撤回图片/名称/位置,但后台工作人员不会运行如果我尝试在实际的backgroundworker线程中进行,那么列表就不会填充。

   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 + ")";

任何帮助都会受到赞赏,如果它无法从UI线程中卸载工作,我现在很难看到使用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 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:0)

您正在破坏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。似乎没有理由等待控件呈现您的更改。