Oczekiwanie na zakończenie metody

0

Jak już wspominałem w poprzednim temacie robię sobie mały projekt, który ma za zadanie wyciągać pewne dane ze strony internetowej. Problem w tym, że aby pracować na pobranych danych muszę je najpierw pobrać a to chwilę trwa. W związku z tym chciałbym się zapytać jak zagwarantować, że jakaś część kodu może się zacząć wykonywać dopiero po wykonaniu innej części/metody? Na razie jakoś udało mi się to obejść, ale pewnie jest to bardzo złe rozwiązanie i wolałbym to zrobić w jakiś lepszy sposób. Próbowałem się bawić Taskami, ale jeszcze tego do końca nie ogarnąłem.

Klasa pobierająca dane:

public class DivPuller
    {
        private static string websiteAddress=null;
        private static string result = null;

        public DivPuller(string website)
        {
            websiteAddress = website;
            
        }


        public async Task<string> ExtractStringFromDiv()
        {
            HttpClient http = new HttpClient();
            var response = await http.GetByteArrayAsync(websiteAddress);
            String source = Encoding.GetEncoding("UTF-8").GetString(response, 0, response.Length - 1);
            source = WebUtility.HtmlDecode(source);
            HtmlDocument resultDocument = new HtmlDocument();
            resultDocument.LoadHtml(source);

            List<HtmlNode> teamslist =
                resultDocument.DocumentNode.Descendants()
                    .Where(
                        x =>
                            (x.Name == "div" && x.Attributes["class"] != null &&
                             x.Attributes["class"].Value.Contains("box-shiny-alt")))
                    .ToList();
            var node = teamslist[0];

            result = node.InnerText;
            return result;
        }

        public string GetResult()
        {
            return result;
        }
    } 

Klasa parsująca pobrane dane:

 public class DataParser
    {

        private string ToBeParsed = null;
        private string TeamOneName = null;
        private string TeamTwoName = null;

        private string WinningTeamName = null;

        private int TeamOneOdds = 0;
        private int TeamTwoOdds = 0;

        private string MatchFormat = null;

        public DataParser(string tobeparsed)
        {
            this.ToBeParsed = tobeparsed;

            ToBeParsed = Regex.Replace(ToBeParsed, @"[^\u0020-\u007E]", string.Empty);
            string newWord = ToBeParsed.Replace(" ", String.Empty);
            int start = newWord.IndexOf("CET") + "CET".Length;
            int end = newWord.LastIndexOf('%');
            string wynik = newWord.Substring(start, end - start);

            //pelen wynik meczu z oddsami i nazwami teamow
            string wynik1 = wynik.Replace("%", string.Empty);


            int format = newWord.IndexOf("Bestof");
            string matchFormat = newWord.Substring(format + "Bestof".Length);

            //gotowy format meczu
            string matchFormatResult = matchFormat.Substring(0, 1);
            MatchFormat = matchFormatResult;

            
            

            
            string team1withodds = wynik1.Split(new string[] { "vs" }, StringSplitOptions.None)[0];
            string team2withodds = wynik1.Split(new string[] { "vs" }, StringSplitOptions.None)[1];

            //wychwytuje oddsy team1
            Match myMatch;
            MatchCollection matchesCollection = Regex.Matches(team1withodds, @"\d+");
            if (matchesCollection.Count > 1)
            {
                myMatch = matchesCollection[1];
            }
            else
            {
                myMatch = matchesCollection[0];
            }

            TeamOneOdds = int.Parse(myMatch.Value);
            //wychwytuje oddsy team2
            Match myMatch1;
            MatchCollection matchesCollection1 = Regex.Matches(team2withodds, @"\d+");
            if (matchesCollection1.Count > 1)
            {
                myMatch1 = matchesCollection1[1];
            }
            else
            {
                myMatch1 = matchesCollection1[0];
            }

            TeamTwoOdds = int.Parse(myMatch1.Value);

            int team1odds = Int32.Parse(Regex.Match(team1withodds, @"\d+").Value);
            int team2odds = Int32.Parse(Regex.Match(team2withodds, @"\d+").Value);
            
            string team1name = team1withodds.Replace(myMatch.Value, "");
            string team2name = team2withodds.Replace(myMatch1.Value, "");
            

            string winningteam = "Tie";
            string team1wowin;
            string team2wowin;
            //wywalenie wina z nazw
            if (team1name.Contains("(win)"))
            {
                team1wowin = team1name.Replace("(win)", "");
                winningteam = team1wowin;
            }

            else
            {
                team1wowin = team1name;
            }

            if (team2name.Contains("(win)"))
            {
                team2wowin = team2name.Replace("(win)", "");
                winningteam = team2wowin;
            }

            else
            {
                team2wowin = team2name;
            }

            TeamOneName = team1wowin;
            TeamTwoName = team2wowin;

            WinningTeamName = winningteam;

            

        }

        public string GetTeamOneName()
        {
            return TeamOneName;
        }

        public string GetTeamTwoName()
        {
            return TeamTwoName;
        }

        public string GetWinningTeamName()
        {
            return WinningTeamName;
        }

        public string GetMatchFormat()
        {
            return MatchFormat;
        }

        public int GetTeamOneOdds()
        {
            return TeamOneOdds;
        }

        public int GetTeamTwoOdds()
        {
            return TeamTwoOdds;
        }


    }

Klasa korzystająca z dwóch poprzednich, w której przydałoby się właśnie zsynchronizować działanie:

public class DatabaseFiller
    {
        private MatchesContext Context = null;

        private CSGOMatch CurrentMatch = null;

        private DivPuller divPuller = null;

        private DataParser dataParser = null;

        private int MatchId;

        private string WebAddress = "http://csgolounge.com/match?m=";

        private int BeginFilling = 0;

        private int EndFilling = 0;



        public DatabaseFiller(int begin, int end)
        {
            BeginFilling = begin;
            EndFilling = end;

            Context=new MatchesContext();

            for (BeginFilling = begin; BeginFilling < EndFilling; BeginFilling++)
            {
                MatchId = BeginFilling;
                string correctwebaddress = WebAddress + MatchId;
                divPuller = new DivPuller(WebAddress + MatchId.ToString());
                Task taskA = Task.Factory.StartNew(() => divPuller.ExtractStringFromDiv());
                Task<string> getRequiredString = divPuller.ExtractStringFromDiv();
                
                taskA.Wait(10000);
                if (taskA.IsCompleted)
                {
                    MessageBox.Show(divPuller.GetResult());
                    dataParser = new DataParser(divPuller.GetResult()); // bez wczesniejszgo MessageBoxa tutaj dostaję nulla
                    //dataParser = new DataParser(content);
                    CurrentMatch = new CSGOMatch()
                    {
                        TeamOneName = dataParser.GetTeamOneName(),
                        TeamTwoName = dataParser.GetTeamTwoName(),
                        MatchFormat = dataParser.GetMatchFormat(),
                        TeamOneOdds = dataParser.GetTeamOneOdds(),
                        TeamTwoOdds = dataParser.GetTeamTwoOdds(),
                        WinningTeamName = dataParser.GetWinningTeamName(),

                    };

                    Context.CsgoMatches.Add(CurrentMatch);
                    Context.SaveChanges();

                }
                else
                {
                    MessageBox.Show("Timed out");
                }
                
            }
            


        }

        
    } 

Problem polega na tym, że ta metoda:

divPuller.ExtractStringFromDiv(); 

chwilę trwa i w efekcie dostaję nulla jako argument dla:

dataParser = new DataParser(divPuller.GetResult()); 

Wstawienie MessageBoxa daje czas na pobranie i obróbkę danych, no ale tak to to chyba nie powinno wyglądać. Jak więc "nakazać" programowi oczekiwanie na wykonanie się metody:

divPuller.ExtractStringFromDiv();  

i dopiero przejsćie do dalszego wykonywania się?

0
  1. Nie rozumiem czemu robisz praktycznie to samo dwa razy:
Task taskA = Task.Factory.StartNew(() => divPuller.ExtractStringFromDiv());
Task<string> getRequiredString = divPuller.ExtractStringFromDiv();
  1. Czemu odpalasz to w konstruktorze? Czemu aż tyle rzeczy robisz w jednym i drugim kostruktorze.
  2. Czemu wciągasz dane poprzez GetResult skoro ExtractStringFromDiv zwraca dokładnie to samo?
  3. Poczytaj jak działają async i await. To powinno wyglądać mniej więcej tak:
Task<string> getRequiredString = divPuller.ExtractStringFromDiv();
// coś...
string url = await getRequiredString;
// przekaż url dalej...
  1. Pomieszały Ci się te Task kompletnie. Porozdzielaj kod na mniejsze kawałki, funkcje, wyczyść konstruktory ze śmieci. Przemyśl całość jeszcze raz.
0

@DibbyDum Dzięki za odpowiedź.

DibbyDum napisał(a):
  1. Nie rozumiem czemu robisz praktycznie to samo dwa razy:
Task taskA = Task.Factory.StartNew(() => divPuller.ExtractStringFromDiv());
Task<string> getRequiredString = divPuller.ExtractStringFromDiv();

To akurat są tylko pozostałości po nieudanych próbach :)

DibbyDum napisał(a):
  1. Czemu odpalasz to w konstruktorze? Czemu aż tyle rzeczy robisz w jednym i drugim kostruktorze.

Bo do niczego innego te obiekty nie są mi potrzebne. Ale ok, z samego pytania rozumiem, że mam to na metody rozbić, zgadza się?

DibbyDum napisał(a):
  1. Czemu wciągasz dane poprzez GetResult skoro ExtractStringFromDiv zwraca dokładnie to samo?

J.w. ExtractStringFromDiv na początku nic nie zwracał, jak będę wiedział jak poprawnie z tego pobrać wartość to GetResult leci do kosza.

DibbyDum napisał(a):
  1. Poczytaj jak działają async i await. To powinno wyglądać mniej więcej tak:
Task<string> getRequiredString = divPuller.ExtractStringFromDiv();
// coś...
string url = await getRequiredString;
// przekaż url dalej...

Dzięki.

DibbyDum napisał(a):
  1. Pomieszały Ci się te Task kompletnie. Porozdzielaj kod na mniejsze kawałki, funkcje, wyczyść konstruktory ze śmieci. Przemyśl całość jeszcze raz.
0
Endrew napisał(a):

Bo do niczego innego te obiekty nie są mi potrzebne. Ale ok, z samego pytania rozumiem, że mam to na metody rozbić, zgadza się?

Tak zostaw tam tylko inicjalizacje parametrów resztę wywal do jakiś metod/metody.

0

A taki sposób oczekiwania, znów mój Notepad++ ze skryptami w robocie :-) :

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;

class Example
{
    static void Main() 
    {
        Thread t1 = new Thread(() => 
        { 
            Thread.Sleep(1000);
            Console.WriteLine("t1 is ending.");
        });


        Thread t2 = new Thread(() => 
        { 
            Thread.Sleep(5000);
            Console.WriteLine("t2 is ending.");
        });
        
        t2.Start();
        Console.WriteLine("t2.started.");
        t2.Join();
        Console.WriteLine("t2.Join() returned.");
        t1.Start();        
        Console.WriteLine("t1.started.");
        t1.Join();
        Console.WriteLine("t1.Join() returned.");

    }
}

a to wynik z konsoli :

t2.started.
t2 is ending.
t2.Join() returned.
t1.started.
t1 is ending.
t1.Join() returned.

Albo jeszcze inaczej aby już nic nie blokować oczekiwaniem, wprowadzić wątek wątków t3 :

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;

class Example
{
    static void Main() 
    {
        Thread t1 = new Thread(() => 
        { 
            Thread.Sleep(1000);
            Console.WriteLine("t1 is ending.");
        });


        Thread t2 = new Thread(() => 
        { 
            Thread.Sleep(5000);
            Console.WriteLine("t2 is ending.");
        });
        
        Thread t3 = new Thread(() =>
        {
        t2.Start();
        Console.WriteLine("t2.started.");
        t2.Join();
        Console.WriteLine("t2.Join() returned.");
        t1.Start();        
        Console.WriteLine("t1.started.");
        t1.Join();
        Console.WriteLine("t1.Join() returned.");
        Console.WriteLine("t3 is ending.");
        });
        
        t3.Start();
        Console.WriteLine("t3.Uruchamiam");

    }
}

wynik wówczas jest :

t3.Uruchamiam
t2.started.
t2 is ending.
t2.Join() returned.
t1.started.
t1 is ending.
t1.Join() returned.
t3 is ending.

pozdr AK

1 użytkowników online, w tym zalogowanych: 0, gości: 1