【问题标题】:The synchronous behavior of The Multi-Threaded Method多线程方法的同步行为
【发布时间】:2015-09-30 12:27:24
【问题描述】:

经过长时间的工作,我无法通过multi-threading 使用这种方法获得任何成功。非常感谢您的帮助。

我有一个listview,其中第一列包含我们稍后在其他方法中需要的网址。秒列是结果。

我正在创建两个带有 url 的 ArrayLists 并将它们(带有 arraylist 的 url)发送到该方法,然后在该方法中,使用 httpwebrequest,我从每个 url 获取页面标题。

所以问题是当我尝试触发 5 threads 时,它的工作方式如下:

(U:url,R:结果)

u1 = r1 (所以,我必须得到 u1 的结果为 r1..)

但我越来越喜欢:

u1 = r1, u2 = r1, u3(or4) = r1, u4 = r1, u5=r2(or3,4)

但我期待的是: u1=r1, u2=r2, u3=r3, u4=r4...

为了更好的解释,请查看下面的图片

然后我尝试将Lock 关键字与私有对象一起使用,但随后我丢失了multi-threading。它的工作原理与我一样,但一一例外。不要同时触发 5 个不同的线程并等待完成线程 1 以移动到下一个线程。

///主类///

/* A store of all created threads. */
ArrayList _threads = new ArrayList();
/* A store of all FileDownloader objects. */
ArrayList _instances = new ArrayList();
private int _activeDownloadCount = 0;
object _lockObject = new object();

按钮:

_instances = new ArrayList();
_threads = new ArrayList();
_activeDownloadCount = 0;

FileDownloader download = null;
foreach (ListViewItem item in listviewUrl.Items)
{               
  item.SubItems[9].Text = "Not started";        

    download = new FileDownloader(item.SubItems[0].Text);               
    item.Tag = download;                
    try
    {
       ThreadStart tsDelegate = new ThreadStart(download.Download);
       download.DownloadStarting += new FileDownloader._delDownloadStarting(download_DownloadStarting);
       download.DownloadCompleted += new FileDownloader._delDownloadCompleted(download_DownloadCompleted);

       Thread t = new Thread(tsDelegate);
       t.Name = item.SubItems[0].Text;
       _threads.Add(t);
       _instances.Add(download); 
    }
    catch
    {

        item.SubItems[9].Text = "Error";
    }
}
StartDownload();

开始下载方法:

int j = 0;
int limit = int.Parse(numThreadSearch.Text);
int iCount = 0;
lock (_lockObject)
{
    iCount = _instances.Count;
}
if (iCount != 0)
{
    foreach (Thread thread in _threads)
    {
        FileDownloader file = ((FileDownloader)_instances[j]);
        if (file._IsStarted == false)
        {
            lock (_lockObject)
            {
                thread.Start();

                Console.WriteLine(_activeDownloadCount);
                _activeDownloadCount++;
            }
        }
        if (_activeDownloadCount == limit)
        {
            break;
        }
        j++;
    }
}
else
{
    /* If all the files have downloaded, we will do something here.
}

方法完成时:

 void download_DownloadCompleted(FileDownloader thread, bool isSuccess)
        {

            lock (_lockObject)
            {
                _activeDownloadCount--;

            }

            PageRankReturns(FileDownloader._PageRankReturn, thread);
            RemoveFromInternalPool(thread);
            StartDownload();

        }


         delegate void delSetStatus(string Status, FileDownloader f);

     private void PageRankReturns(string Status, FileDownloader f)
            {
                if (listviewUrl.InvokeRequired)
                {
                    delSetStatus s = new delSetStatus(PageRankReturns);
                    this.Invoke(s, new object[] { Status, f });
                }
                else
                {
                    foreach (ListViewItem item in listviewUrl.Items)
                    {
                        if (item.Tag == f)
                        {
                            /* Use locking to synchronise across mutilple thread calls. */
                            lock (_lockObject)
                            {
                                item.SubItems[2].Text = Status;

                            }
                            break;
                        }
                    }
                }
            }



 private void RemoveFromInternalPool(FileDownloader thread)
        {
            int i = 0;
            foreach (FileDownloader f in _instances)
            {
                if (f == thread)
                {
                    /* If the file has downloaded, remove it from our pool. */
                    lock (_lockObject)
                    {
                        _threads.Remove(_threads[i]);
                        _instances.Remove(f);
                        break;
                    }
                }
                i++;
            }
        }

/// 二等舱 ///

#region  Fields

private string _DocumentUrl = string.Empty;
private string _DirectoryPath = string.Empty;
public bool _IsDownloading = false;
public bool _IsDownloadSuccessful = false;

public bool _IsStarted = false;

#endregion

#region Delegates
public delegate void _delDownloadStarting(FileDownloader thread);
public delegate void _delDownloadCompleted(FileDownloader thread, bool isSuccess);
public delegate void _delDownloadCWorking(FileDownloader thread);

#endregion

#region Events
public event _delDownloadStarting DownloadStarting;
public event _delDownloadCompleted DownloadCompleted;

protected static readonly object locker = new object();
public static string pageTitleResult= string.Empty;

public static string _pageTitleResult
{
    get { return pageTitleResult; }
}

public FileDownloader(string documentUrl)
{
    _DocumentUrl = documentUrl;
}

//下载方法 //

public void Download()
{
    _IsStarted = true;
    DownloadStarting(this);            
    _IsDownloading = true;
    _IsDownloadSuccessful = false;

    // with lock keyword it works one by one//
    //without lock or monitor.enter keyword then it works as i tried explain above u1 = r1, u2 = r1, ur3 = r1, u4=r3 etc...

    try
    {
        string pageHtml = getHtml(_DocumentUrl);
        HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();

        //html agibility works with returned string from gethtml...
      // string pageTitle = html agibility work result, it's string...
       pageTitleResult = pageTitle 

        _IsDownloadSuccessful = true;
        _IsDownloading = false;
        /* raise a download completed event. */
        DownloadCompleted(this, _IsDownloadSuccessful);
    }
    catch
    {
        _IsDownloadSuccessful = false;
    }
   Thread.Sleep(10);
}

锁定对象:

protected static readonly object locker = new object();

好吧,我只是在excel中做了一个例子来展示它是如何工作的......

这是没有 Lock 关键字的返回:

这是带有 lock 关键字的,顺便说一下,在这个例子中,触发了 5 个线程,但它等待第一个线程的结束......

这就是我想要做的...

【问题讨论】:

  • "pageTitleResult = pageTitle;" - 什么是“pageTitleResult”、“pageTitle”?它们的定义在哪里以及在哪里使用?
  • @AlexanderKiselev 我刚刚编辑了这部分。它只是 html 敏捷包的工作,不管有没有它,线程问题都不会改变,这就是为什么我删除了这部分以稍微清除代码。
  • 我在你的代码中找不到,你在 ListView 列中插入字符串:item.Subitems[1],url 在哪里,item.Subitems[2],结果在哪里,如果我能正确理解你的帖子。
  • @AlexanderKiselev 感谢您抽出宝贵时间,我刚刚添加了用于多线程的其他必要方法。
  • 在方法 PageRankReturns() 中,尝试“if (f.Equals(item.Tag))”而不是“if (item.Tag == f)”。

标签: c# multithreading listview arraylist


【解决方案1】:

我认为,这个方法有问题:

private void PageRankReturns(string Status, FileDownloader f)
{
            if (listviewUrl.InvokeRequired)
            {
                delSetStatus s = new delSetStatus(PageRankReturns);
                this.Invoke(s, new object[] { Status, f });
            }
            else
            {
                foreach (ListViewItem item in listviewUrl.Items)
                {
                    if (item.Tag == f)
                    {
                        /* Use locking to synchronise across mutilple thread calls. */
                        lock (_lockObject)
                        {
                            item.SubItems[2].Text = Status;

                        }
                        break;
                    }
                }
            }
}

您必须更改应用程序的结构。 请参阅上面的方法(“PageRankReturns”)。您对列表视图的所有项目运行一个循环,以找到 FileDownloader 的项目。为什么 FileDownloader 对他的项目一无所知?如果 FileDownloader 知道他的项目,那么你就不需要这个循环。您将在 FileDownloader 和他的 Item 之间建立一个明确的链接。

【讨论】:

  • 结果是一样的。我认为最好检查所有代码。从这里:codeproject.com/Articles/14219/…。对于我所做的更改,您可以从上面看到。
  • 好吧,我想我会继续并行,我仍然无法解决问题。 @alexanderkiselev
猜你喜欢
  • 2017-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 2023-02-10
  • 2012-11-15
  • 1970-01-01
  • 2018-12-09
相关资源
最近更新 更多