Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Получение данных из потока


Автор: arsenium 1.6.2011, 18:12
Здравствуйте! Подскажите куда копать, разбираюсь с потоками в c#. 
И возник такой вопрос, к примеру есть метод читающий обычный txt файл в переменную, и это чтение происходит в отдельном потоке, как получить доступ к этой переменной из главного потока?

К примеру метод:
Код

public class Test
    {
      public string FileText;
      public void ReadFile()
        {
         //читаем файл в переменную string.
         FileText=...
        }
    }

И вызываем этот метод в потоке:

Код

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

private void button1_Click(object sender, EventArgs e)
        {
         Test TestRead = new Test();
         new Thread(TestRead.ReadFile).Start();
         TextBox1.Text=...
        }
}

Вот к примеру, если такой код, и мне нужно в классе Form1 после выполнения потока достать то, что записалось в переменную FileText и вывести это ее в TextBox1 на форме.

Автор: KuMa1104 1.6.2011, 20:40
ну как самый простой вариант хранить данные в какой то переменной и передать. 
Код

public string ReadFile()
        {
         string output;
         //читаем файл в переменную string.
         output=...

         return output;
        }


В форме сделать отдельную ф-ию
Код

void DoWork()
{
       string FileText =  TestRead.ReadFile();

        TextBox1.Invoke((MethodInvoker)delegate{TextBox1.Text = FileText }) //анонимный делегат
}


вызываеш ф-ию
Код

private void button1_Click(object sender, EventArgs e)
        {
         Test TestRead = new Test();
         new Thread(DoWork).Start();
        }


Автор: KuMa1104 1.6.2011, 20:55
А вообще нафиг отделбную ф-ию её тоже в делегат пихай)

Автор: Dennnis 2.6.2011, 05:27
В приведенном тобой примере доступ к считанным данным можно получить так:
Код

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

private void button1_Click(object sender, EventArgs e)
        {
         Test TestRead = new Test();
         new Thread(TestRead.ReadFile).Start();
         Thread.Sleep(100); //Подождать пока считаются данные
         TextBox1.Text=TestRead.FileText;
        }
}

 smile 

Автор: Экскалупатор 2.6.2011, 08:51
Dennnis, а какой в этом смысл? зачем запускать чтение в отдельном потоке и ждать пока оно закончится? параллельные потоки нужны тогда когда нельзя ждать, и нужно что бы задача выполнилась в фоне пока приложение выполняет какую то другую задачу. а у тебя получилось что приложение запускает поток, а потом просто ждет пока поток закончит читать. к тому же ты уверен что отведенного тобой времени хватит на то что бы закончить чтение? пока что может и хватит, а в будущем?

Автор: arsenium 2.6.2011, 10:50
KuMa1104, хм, спасибо большое.
но тут я так понял сразу в textBox результат выводит, а сильно усложнится если сначала присвоить результат промежуточной переменной TempText, а только потом присвоить ее TextBox1.Text, так ведь по идее можно?

Код

void DoWork()
{
       string TempText="";
       string FileText =  TestRead.ReadFile();

        //TextBox1.Invoke((MethodInvoker)delegate{TextBox1.Text = FileText; });
        //TempText

        //какие-то действия с TempText.
        TextBox1.Text=TempText;
}


Цитата

А вообще нафиг отделбную ф-ию её тоже в делегат пихай)

А тут немного не понял...

Автор: jonie 2.6.2011, 12:11
лучше вместо создания Thread-а (как в примерах выше) использовать либо BackgroundWorker либо Task

Автор: KuMa1104 2.6.2011, 12:45
Цитата(arsenium @  2.6.2011,  10:50 Найти цитируемый пост)
так ведь по идее можно?

так по идее плохо. У тебя TextBox1 был создан в другом потоке. это crossthread. В debug будет вообще кидать exception.

TextBox1.Invoke // почитай он как раз и вызывает поток которому принадлежит.

разберись с этим это очень полезная штука. и про BackgroundWorker тоже почитай .jonie прав это оч сильная штука.

Автор: Dennnis 2.6.2011, 14:10
Цитата(Экскалупатор @  2.6.2011,  08:51 Найти цитируемый пост)
Dennnis, а какой в этом смысл? зачем запускать чтение в отдельном потоке и ждать пока оно закончится? параллельные потоки нужны тогда когда нельзя ждать, и нужно что бы задача выполнилась в фоне пока приложение выполняет какую то другую задачу. а у тебя получилось что приложение запускает поток, а потом просто ждет пока поток закончит читать. к тому же ты уверен что отведенного тобой времени хватит на то что бы закончить чтение? пока что может и хватит, а в будущем? 

Нету смысле) Я просто написал как получить доступ к данным записанных другим потокомsmile

Автор: Экскалупатор 2.6.2011, 17:58
Dennnis, а ждать зачем?

Автор: arsenium 2.6.2011, 22:49
Цитата(jonie @  2.6.2011,  12:11 Найти цитируемый пост)
лучше вместо создания Thread-а (как в примерах выше) использовать либо BackgroundWorker либо Task

А немог бы кто-нибудь набрасать пример для моего случая с использованием BackgroundWorker? и объяснить разницу с использованием Thread?

Автор: Dennnis 3.6.2011, 05:12
Цитата(Экскалупатор @  2.6.2011,  17:58 Найти цитируемый пост)
Dennnis, а ждать зачем? 

Если не ждать, то можно получить некорректные данные, потому что поток не успеет завершить запись в переменную

Автор: Экскалупатор 3.6.2011, 09:42
Dennnis, ага, и я про это говорю, приложение должно дождаться выполнения потока(кстати, не факт что того времени что ты отвел хватит, про это я уже тоже говорил). и получается что смысла во всем этом нету. точно такая же логика получиться если не использовать второй поток вообще, а все сделать в основном потоке. программа не будет выполняться дальше пока не закончит читать данные. возвращаясь к тому с чего я начал: так для чего нужно использовать второй поток? выигрыша с него нету, только проблемы с реализацией.

Автор: jonie 3.6.2011, 11:55
вот набрасал быстро

на форме две кнопки и textbox (первая стартует процесс, вторая cancel-ит). Больше ниче делать не надо (разве что имя файла поменять) 8-)

Для удобства работа вынесена в отдельный класс.
Код

        public Form1()
        {
            InitializeComponent();
            button2.Enabled = false;
            button1.Click += button1_Click;
            button2.Click += button2_Click;
        }

        TxtSlowReader _txtReader;

        private void button1_Click(object sender, EventArgs e)
        {
            _txtReader = new TxtSlowReader("C:\\textfile.txt");
            _txtReader.OnComplete += txtReader_OnComplete;
            _txtReader.WorkAsync();
            button1.Enabled = false;
            button2.Enabled = true;
        }

        void txtReader_OnComplete(object sender, TxtSlowReaderEventArgs e)
        {
            button1.Enabled = true;
            button2.Enabled = false;

            if (e.Error != null)
                MessageBox.Show(e.Error.ToString(), "ERROR!");
            else if(e.Lines==null)
                MessageBox.Show("Canceled!", "ok");
            else if (e.Lines != null)
                textBox1.Text = string.Join(", ", e.Lines);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (null != _txtReader)
            {
                _txtReader.Abort();
                _txtReader = null;
            }
        }
    }

    public class TxtSlowReaderEventArgs : EventArgs
    {
        public TxtSlowReaderEventArgs(Exception error, IList<string> lines)
        {
            this.Error = Error;
            this.Lines = lines;
        }

        public Exception Error { get; private set; }
        public IList<string> Lines { get; private set; }
    }

    public class TxtSlowReader
    {
        private string _filename;
        private BackgroundWorker _bw;
        public TxtSlowReader(string filename)
        {
            _filename = filename;
        }

        public void Abort()
        {
            lock (this)
            {
                if (_bw != null)
                {
                    _bw.CancelAsync();
                    _bw = null;
                }
            }
        }

        public void WorkAsync()
        {
            _bw = new BackgroundWorker();
            _bw.WorkerSupportsCancellation = true;
            _bw.DoWork += bw_DoWork;
            _bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            _bw.RunWorkerAsync();
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (null != OnComplete)
            {
                if (e.Cancelled)
                    OnComplete(this, new TxtSlowReaderEventArgs(null, null));
                else
                    OnComplete(this, new TxtSlowReaderEventArgs(e.Error, (IList<string>)e.Result));
            }
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> rv = new List<string>();
            using (var fs = new FileStream(_filename, FileMode.Open, FileAccess.Read))
            {
                using (var txtr = new StreamReader(fs))
                {
                    string line;
                    while ((line = txtr.ReadLine()) != null)
                    {
                        lock (this)
                        {
                            if (_bw == null || _bw.CancellationPending) { rv = null; break; }
                        }

                        Thread.Sleep(200);
                        rv.Add(line);
                    }
                }
            }
            e.Result = rv;
        }

        public event EventHandler<TxtSlowReaderEventArgs> OnComplete;
    }

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)