1

私はサッカーシミュレーターに取り組んでおり、別のスレッドで背景に9つの試合があります。そして、各スレッドの中心にあるメソッドには、イベントがあります。そして、それが発生した場合 (ゴールが「キック」された場合)、フォームのラベル (goalLabel という名前) を部分的な結果で更新したいと考えています。私はコードを書きました...:

for (int i = 0; i < 6; i++)
{
    if (i % 2 == 0) homeGoals++;
    else awawyGoals++;
    if (goal != null) goal(this); //(goal is an event)
    Thread.Sleep(1000);
} //this is the full method

...各試合でゴールの正確な数は 6 (結果は 3 - 3) になるため、9 (9 も修正) のバックグラウンド マッチでは、goalLabel はテキスト (6*9=) を変更する必要があります。 54回。ただし、数回しか変更されません。イベントの eventhandler メソッドは次のとおりです。

public void GoalEventHandler(Match match)
{
    string akt = string.Format("{0} {1} - {2} {3}", match.Opps[0].Name, match.Hgoals, match.Agoals, match.Opps[1].Name);
    UpdateGoalLabel(akt);
}

そして UpdateGoalLabel メソッド:

public void UpdateGoalLabel(string update)
{
    if (InvokeRequired)
    {
        MyDel del = new MyDel(UpdateGoalLabel); // yeah, I have a delegate for it: delegate void MyDel(string update);
        Invoke(del, update);
    }
    else
    {
        lock (this) // if this lock isn't here, it works the same way
        {
            this.goalLabel.Text = update;
        }
    }
}

だから私はラベルのテキストに到達して変更することができますが、54回変更されない理由はわかりません. そして、それが目標であり、すべての目標の後に通知を受け取ることです。

何か案が?

前もって感謝します。

更新 #1: VS2010 を使用しています。

スレッドを起動するコードは次のとおりです。

List<Thread> allMatches = new List<Thread>();
foreach (Match match in matches)
{
    Thread newtmatch = new Thread(match.PlayMatch); //this is the first code block I wrote
    allMatches.Add(newtmatch);
    newtmatch.Start();
}

更新 #2: イベント ハンドラをアタッチする場所は次のとおりです (これは同じメソッドで、前のコード ブロックの数行上にあります)。

matches = new List<Match>();
foreach (Team[] opponents in Program.cm.nextMatches)
{
    Match vmi = new Match(opponents);
    matches.Add(vmi);
    vmi.goal += new Match.goalevent(GoalEventHandler);
}
//Program.cm.nextMatches is a List<Team[]> object that contains the pairs of teams for the next matches;

そして、これらの Team 配列を Match オブジェクトに変換します。このクラスには 2 つの Team フィールドがあり、イベントと、最初のコード ブロック (のみ) を含むメソッドである PlayMatch メソッドがあるからです。

4

2 に答える 2

0

私もUIの更新に問題があり、「Thread」クラスだけでなく「BackgroundWorker」スレッドを直接使用しました。BackgroundWorker スレッドを使用すると、進行状況の変更を明示的に報告し、スレッドの外部で何らかのメソッドを呼び出すことができます (つまり、セカンダリ スレッドを呼び出した呼び出し元の UI スレッド)。したがって、バックグラウンドワーカーから派生したカスタムクラスを作成し、説明した「Match」クラスの独自のバージョンも作成しました

public class Match
{
    public Match( string Home, string Away )
    {
        HomeTeam = Home;
        HomeGoals = 0;

        AwayTeam = Away;
        AwayGoals = 0;
    }

    // simple properties with PROTECTED setters, yet readable by anyone
    public string HomeTeam
    { get; protected set; }

    public int HomeGoals
    { get; protected set; }

    public string AwayTeam
    { get; protected set; }

    public int AwayGoals
    { get; protected set; }

    // just place-holder since I don't know rest of your declarations
    public EventHandler goal;

    public void PlayMatch()
    {
        for (int i = 0; i < 6; i++)
        {
            if (i % 2 == 0) 
                HomeGoals++;
            else 
                AwayGoals++;

            // Report to anyone listening that a score was made...
            if (goal != null) 
                goal(this, null);

            Thread.Sleep(1000);
        } 
    }
}

// Now, the background worker
public class MatchBGW : BackgroundWorker
{
    // each background worker preserves the "Match" instance it is responsible for.
    // this so "ReportProgress" can make IT available for getting values.
    public Match callingMatch
    { get; protected set; }

    // require parameter of the match responsible for handling activity
    public MatchBGW(Match m)
    {
        // preserve the match started by the background worker activity
        callingMatch = m;

        // tell background worker what method to call
        // using lambda expression to cover required delegate parameters 
        // and just call your function ignoring them.
        DoWork += (sender, e) => m.PlayMatch();

        // identify we can report progress  
        WorkerReportsProgress = true;

        // Attach to the match.  When a goal is scored, notify ME (background worker)
        m.goal += GoalScored;
    }

    // this is called from the Match.
    public void GoalScored(object sender, EventArgs e)
    {
        // Now, tell this background worker to notify whoever called IT
        // that something changed.  Can be any percent, just something to
        // trigger whoever called this background worker, so reported percent is 1
        ReportProgress(1);
    }
}

ここで、ボタンのクリックから開始されるなど、ラベルを持つ呼び出しウィンドウから...

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        // create your "Matches" between teams
        matches = new List<Match>();
        matches.Add(new Match("A", "B"));
        matches.Add(new Match("C", "D"));
        matches.Add(new Match("E", "F"));

        foreach (Match m in matches)
        {
            // create an instance of background worker and pass in your "match"
            MatchBGW bgw = new MatchBGW(m);
            // tell the background worker that if it is notified to "
            // report progress" to, to pass itself (background worker object)
            // to this class's SomeoneScored method (same UI thread as textbox)
            bgw.ProgressChanged += SomeoneScored;
            // Now, start the background worker and start the next match
            bgw.RunWorkerAsync();
        }
    }

    // This is called from the background worker via "ReportProgress"
    public void SomeoneScored(object sender, ProgressChangedEventArgs e)
    {
        // Just ensuring that the background worker IS that of what was customized
        if (sender is MatchBGW)
        {
            // get whatever "match" associated with the background worker
            Match m = ((MatchBGW)sender).callingMatch;
            // add it's latest score with appropriate home/away team names
            this.txtAllGoals.Text +=
                string.Format("{0} {1} - {2} {3}\r\n", 
                m.HomeTeam, m.HomeGoals, m.AwayGoals, m.AwayTeam );
        }
    }

はい、それはより多くのコードかもしれませんが、コールバックされた人を明示的に処理し、適切なスレッドで何を報告しています... BeginInvoke はテスト/アクションを必要としません。あなたの問題に代わるものです。

于 2012-11-26T05:29:23.823 に答える
0

あなたが何をしているのか、私が理解していることを確認させてください。テキスト ボックスを更新して 1 秒間スリープするたびに、6 回ループする 9 つのスレッドを開始しました。その音から、彼らはすべて同じレーベルを更​​新しています。更新をリストにプッシュすると、すべてを取得できるとコメントしましたが、私の推測では、9 つ​​のスレッドすべてが非常に高速に更新されているため、それを見ることができず、最後のスレッドが優先されます。

UI をどのように構築しているかはわかりませんが、これは WPF と MVVM(Model-View-ViewModel) に最適だと思います。よほどの理由がない限り、私は WinForms を使用しません。WPF の方がはるかに簡単に操作できます。

いくつかのビュー モデルを作成します。

public class MainWindowViewModel : DispatcherObject, INotifyPropertyChanged
{
    public MainWindowViewModel()
    {
        Matches = new ObservableCollection<MatchViewModel>();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection<MatchViewModel> _matches;
    public ObservableCollection<MatchViewModel> Matches
    {
        get { return _matches; }
        set
        {
            _matches = value;
            OnPropertyChanged("Matches");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class MatchViewModel : DispatcherObject, INotifyPropertyChanged
{
    public MatchViewModel()
    {
        HomeTeam = new TeamViewModel();
        AwayTeam = new TeamViewModel();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private TeamViewModel _homeTeam;
    public TeamViewModel HomeTeam
    {
        get { return _homeTeam; }
        set
        {
            _homeTeam = value;
            OnPropertyChanged("HomeTeam");
        }
    }

    private TeamViewModel _awayTeam;
    public TeamViewModel AwayTeam
    {
        get { return _awayTeam; }
        set
        {
            _awayTeam = value;
            OnPropertyChanged("AwayTeam");
        }
    }

    public void PlayMatch()
    {
        for (int i = 0; i < 6; i++)
        {
            if (i % 2 == 0)
                OnGoalScored(HomeTeam);
            else
                OnGoalScored(AwayTeam);
            Thread.Sleep(1000);
        }
    }

    private void OnGoalScored(TeamViewModel team)
    {
        if (!team.Dispatcher.CheckAccess())
        {
            team.Dispatcher.Invoke((Action<TeamViewModel>)OnGoalScored, team);
        }
        else
        {
            team.Score++; // do other stuff here
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class TeamViewModel : DispatcherObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    private int _score;
    public int Score
    {
        get { return _score; }
        set
        {
            _score = value;
            OnPropertyChanged("Score");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

次に、UI スレッドのプログラム クラスで、次のようにします。

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        MainWindowViewModel mainWindow = new MainWindowViewModel();
        List<Thread> matchThreads = new List<Thread>();
        foreach (Team[] opponents in Program.cm.nextMatches)
        {
            MatchViewModel match = new MatchViewModel();
            match.HomeTeam.Name = opponents[0].Name;
            match.AwayTeam.Name = opponents[1].Name;
            mainWindow.Matches.Add(match);

            Thread matchThread = new Thread(match.PlayMatch);
            matchThreads.Add(matchThread);
            matchThread.Start();
        }

        MainWindow = new MainWindow();
        MainWindow.DataContext = mainWindow;
        MainWindow.Show();
    }

VS2010 では、プロジェクトを作成するときにスターアップ項目が System.Windows.Application から継承されるため、OnStartup のオーバーライドで私が行いました。

テスト用にこのシンプルな UI があります。

<Window x:Class="TestMatch.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <ItemsControl ItemsSource="{Binding Matches}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} {1} - {2} {3}">
                            <Binding Path="HomeTeam.Name"/>
                            <Binding Path="HomeTeam.Score"/>
                            <Binding Path="AwayTeam.Name"/>
                            <Binding Path="AwayTeam.Score"/>
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

このコードは非常に粗雑であり、これを行う MVVM の方法の簡単なデモを提供するために一緒に投げられます。9 チームすべてを 1 秒ごとに更新することができました。あなたが持っていたオブジェクトをシミュレートするためのフィラーコードがいくつかありました:

public partial class Program
{
    protected override void OnStartup(StartupEventArgs e)
    {
        ...
    }

    private class Team
    {
        public string Name { get; set; }
    }

     private static class cm
     {
         static cm()
         {
             nextMatches =
                 new Team[][]
                     {
                         new[] { new Team { Name = "TeamA" }, new Team { Name = "TeamB" }},
                         new[] { new Team { Name = "TeamC" }, new Team { Name = "TeamD" }},
                         new[] { new Team { Name = "TeamE" }, new Team { Name = "TeamF" }},
                         new[] { new Team { Name = "TeamG" }, new Team { Name = "TeamH" }},
                         new[] { new Team { Name = "TeamI" }, new Team { Name = "TeamJ" }},
                         new[] { new Team { Name = "TeamK" }, new Team { Name = "TeamL" }},
                         new[] { new Team { Name = "TeamM" }, new Team { Name = "TeamN" }},
                         new[] { new Team { Name = "TeamO" }, new Team { Name = "TeamP" }},
                         new[] { new Team { Name = "TeamQ" }, new Team { Name = "TeamR" }},
                     };
         }

         public static Team[][] nextMatches { get; set; }
     }
}
于 2012-11-26T06:25:34.407 に答える