BackGroundWorker一次只允许两个线程

时间:2013-05-16 16:44:54

标签: c# multithreading backgroundworker

好的伙计们,为了解决这个问题,我会在这里放下很多代码,因为我是多线程的新手,我不想遗漏任何东西。

问题:我有一个UserControl,我从供应商处下载数据Feed文件。我希望能够一次处理这几个,但是,我设置BackGroundWorker的方式,它一次只运行两个下载,而其他人似乎“排队等待”一个< / strong>其他两个在开始之前完成。正在等待的那些,使其成为FTPDownload()中的第一个“GetResponse”调用。

如果我取消当前正在运行的任何一个下载,“等待中”的下载永远不会启动。

我在DoWork事件中使用States来决定启动哪个进程,并在WorkComplete事件中读取相同的状态以决定如何结束进程。

除了我所描述的以外,一切都很好。

代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GooeyPC_CCSE;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;

namespace Gooey_Manager
{
    public partial class DFProgressControl : UserControl
    {
        BackgroundWorker _bg_worker = new BackgroundWorker();

        #region Private Members and Public Accessors
        /// <summary>
        /// Get/Set DFProgressControl Vendor Name
        /// </summary>
        public string VendorName
        {
            get { return lblDFP_VendorName.Text; }
            set { lblDFP_VendorName.Text = value; }
        }

        /// <summary>
        /// Get/Set DFProgressControl CurrentState
        /// </summary>
        public string CurrentState
        {
            get { return lblDFP_CurrentState.Text; }
            set { lblDFP_CurrentState.Text = value; }
        }

        /// <summary>
        /// Get/Set DFProgressControl RecordOfNum
        /// </summary>
        public string RecordOfNum
        {
            get { return lblDFP_RecordOfNum.Text; }
            set { lblDFP_RecordOfNum.Text = value; }
        }

        private AffiliateServiceProviderItem _aff_svc_prov = new AffiliateServiceProviderItem();
        public AffiliateServiceProviderItem AffiliateSvcProvider
        {
            get { return _aff_svc_prov; }
            set { _aff_svc_prov = value; }
        }

        private VendorSiteInfoItem _vsi = new VendorSiteInfoItem();
        public VendorSiteInfoItem VendorSiteInfo
        {
            get { return _vsi; }
            set { _vsi = value; }
        }

        /// <summary>
        /// DataFeeder States
        /// </summary>
        public enum ObjectState
        {
            Nothing = 0,
            Canceling = 1,
            Download = 2,
            Unzip = 3,
            Prestage = 4,
            Validate = 5,
            Move_to_DB = 6,
            Destroy = 7
        }
        private ObjectState _feeder_state = ObjectState.Nothing;
        public ObjectState FeederState
        {
            get { return _feeder_state; }
            set { _feeder_state = value; }
        } 
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="vendor_id"></param>
        public DFProgressControl(int vendor_id)
        {
            InitializeComponent();

            //Initialize member data
            this._vsi = GooeyDataFactory.GetVendorSiteInfo(vendor_id);
            this._aff_svc_prov = GooeyDataFactory.GetAffiliatSvcProviderByID(_vsi.AffiliateSvcProviderID);
            VendorName = _vsi.SiteName;
            CurrentState = FeederState.ToString();
            RecordOfNum = "";

            //Background worker settings
            _bg_worker.WorkerSupportsCancellation = true;

            //Background worker event handlers
            _bg_worker.DoWork += new DoWorkEventHandler(workerDataFeeder_DoWork);
            _bg_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerDataFeeder_RunWorkerCompleted);
        }

        private void workerDataFeeder_DoWork(object sender, DoWorkEventArgs e)
        {
            switch (FeederState)
            {
                case ObjectState.Download:
                    this.ProductDataFeedDownload(VendorName);
                    break;
                case ObjectState.Unzip:
                    break;
                default:
                    break;
            }
        }

        private void workerDataFeeder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            switch (FeederState)
            {
                case ObjectState.Download:
                    lblDFP_RecordOfNum.Text = "Download Complete";
                    FeederState = ObjectState.Nothing;
                    break;
                case ObjectState.Unzip:
                    FeederState = ObjectState.Nothing;
                    break;
                case ObjectState.Canceling:
                    lblDFP_RecordOfNum.Text = "Operation Canceled";
                    pbDFP_ProgressBar.Value = 0;
                    FeederState = ObjectState.Nothing;
                    break;
                default:
                    break;
            }
            CurrentState = FeederState.ToString();
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        //Data Feeder Process menu items
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Download vendor's data feed file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_bg_worker.IsBusy)
            {
                MessageBox.Show(VendorName + " Data Feeder is busy with " + CurrentState + "\nCancel Operation to do something else.");
            }
            else
            {
                FeederState = ObjectState.Download;
                CurrentState = FeederState.ToString();
                lblDFP_RecordOfNum.Text = "Preparing to download...";
                pbDFP_ProgressBar.Value = 0;
                _bg_worker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Cancel current operation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cancelOperationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_bg_worker.IsBusy)
            {
                FeederState = ObjectState.Canceling;
            }
            else
            {
                MessageBox.Show("No operation to stop for " + VendorName + " data feeder.");
            }
        }

        private void destroyToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        //Data Feed Stuff
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Download Vendor's data feed
        /// </summary>
        /// <param name="vendor_name"></param>
        /// <param name="pb"></param>
        /// <param name="status_text"></param>
        /// <returns></returns>
        public byte[] ProductDataFeedDownload(string vendor_name)
        {
            string filename = Regex.Match(this._vsi.DFFTPProd, @"(\w|[-.])+$", RegexOptions.IgnoreCase).ToString();
            byte[] data_feed = FTPDownload(this._vsi.DFFTPProd, this._aff_svc_prov.FTPUser, this._aff_svc_prov.FTPPassword);

            if (data_feed.Length != 0)
            {
                //Write the bytes to a file
                //NOTE: Do I really need to be Using Windows.Forms for this. System.IO should have the necessary methods?
                SaveFileDialog save_data_feed = new SaveFileDialog();
                save_data_feed.FileName = this._vsi.DFFileTextProd + "\\" + filename;
                FileStream newFile = new FileStream(save_data_feed.FileName, FileMode.Create);
                newFile.Write(data_feed, 0, data_feed.Length);
                newFile.Close();
            }
            return data_feed;
        }

        /// <summary>
        /// Connects to the FTP server and downloads the file
        /// </summary>
        /// <param name="FTPAddress"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="pb"></param>
        /// <param name="status_text"></param>
        /// <returns></returns>
        private byte[] FTPDownload(string FTPAddress, string username, string password)
        {
            byte[] downloadedData = new byte[0];

            try
            {
                //Create FTP request
                //Note: format is ftp://server.com/file.ext
                FtpWebRequest request = FtpWebRequest.Create(FTPAddress) as FtpWebRequest;

                //Get the file size first (for progress bar)
                request.Method = WebRequestMethods.Ftp.GetFileSize;
                request.Credentials = new NetworkCredential(username, password);
                request.UsePassive = true;
                request.UseBinary = true;
                request.KeepAlive = true; //don't close the connection
                int dataLength = 999999;
                dataLength = (int)request.GetResponse().ContentLength;//This does not work for Tigerdirect download. Causes error.                

                //Now get the actual data
                request = FtpWebRequest.Create(FTPAddress) as FtpWebRequest;
                request.Method = WebRequestMethods.Ftp.DownloadFile;
                request.Credentials = new NetworkCredential(username, password);
                request.UsePassive = true;
                request.UseBinary = true;
                request.KeepAlive = false; //close the connection when done

                //Streams
                FtpWebResponse response = request.GetResponse() as FtpWebResponse;
                Stream reader = response.GetResponseStream();

                //Download to memory
                MemoryStream memStream = new MemoryStream();
                byte[] buffer = new byte[1024]; //downloads in chuncks
                decimal bytesDownloaded = 0;
                bool cancel = false;
                while (!cancel)
                {
                    //Try to read the data
                    int bytesRead = reader.Read(buffer, 0, buffer.Length);

                    if (bytesRead == 0)
                    {
                        break;
                    }
                    else
                    {
                        //Write the downloaded data
                        memStream.Write(buffer, 0, bytesRead);
                        bytesDownloaded += bytesRead;

                        //Show Progress
                        ShowProgress(bytesRead, dataLength);

                        //Check for cancelation request
                        if (this.FeederState == ObjectState.Canceling)
                        {
                            DialogResult result = MessageBox.Show("Click OK to stop current operation for " + VendorName + " data feeder.", "Cancel?", MessageBoxButtons.YesNo);
                            if (result == DialogResult.Yes)
                            {
                                _bg_worker.CancelAsync();
                                cancel = true;
                                memStream = null;
                            }
                            else
                            {
                                this.FeederState = ObjectState.Download;
                            }
                        }
                    }
                }

                //Convert the downloaded stream to a byte array
                if (memStream != null)
                {
                    downloadedData = memStream.ToArray();
                    memStream.Close();
                }

                //Clean up
                reader.Close();
                response.Close();
            }
            catch (Exception e)
            {
            }

            username = string.Empty;
            password = string.Empty;
            return downloadedData;
        }

        /// <summary>
        /// Update progress bar and Record of Num values
        /// </summary>
        /// <param name="progress"></param>
        /// <param name="upper_bound"></param>
        private void ShowProgress(int progress, int upper_bound)
        {
            //Current State
            lblDFP_CurrentState.Invoke((MethodInvoker)delegate
            {
                lblDFP_CurrentState.Text = this.FeederState.ToString();
            });

            //Current Progress
            pbDFP_ProgressBar.Invoke((MethodInvoker)delegate
            {
                pbDFP_ProgressBar.Maximum = upper_bound;
                if (pbDFP_ProgressBar.Value + progress < upper_bound)
                {
                    pbDFP_ProgressBar.Value += progress;
                }
                else
                {
                    pbDFP_ProgressBar.Value = upper_bound;
                }
            });

            //Current value of progress over upper bound
            lblDFP_RecordOfNum.Invoke((MethodInvoker)delegate
            {
                lblDFP_RecordOfNum.Text = (Convert.ToDecimal(pbDFP_ProgressBar.Value)/1024/1024).ToString("0.00") + " of " + (Convert.ToDecimal(upper_bound)/1024/1024).ToString("0.00") + " MB read.";
            });
        }
    }
}

我确信我在这里缺少一些基本的东西,我希望有人可以启发我。就像我说的那样,对线程的东西很新 - 虽然我很高兴我得到了这么远;这不符合我的意图。

提前致谢。

编辑: 我还应该添加如何实例化UserControl

  //Data Feed Tab
private void btnStartDataFeed_Click(object sender, EventArgs e)
{
    DFProgressControl _df_prog_ctl = new DFProgressControl((int)lbDataFeedVendorList.SelectedValue); 
    if (_df_control_list.Find(o => o.VendorSiteInfo.VendorSiteInfoID == (int)lbDataFeedVendorList.SelectedValue) == null)
    {
        //Add instance of object to Data Feeder list
        flowLayoutPanel1.Controls.Add(_df_prog_ctl);
        _df_control_list.Add(_df_prog_ctl);
    }
    else
    {
        MessageBox.Show("There is already an active data feeder for " + GooeyDataFactory.GetVendorSiteInfo(_df_prog_ctl.VendorSiteInfo.VendorSiteInfoID).SiteName + " doing " + _df_prog_ctl.FeederState + ".");
    }
}

1 个答案:

答案 0 :(得分:1)

后台工作人员正常工作。问题是源只允许两个活动下载从同一个IP同时运行。

非常感谢。