关于C#委托的一个问题

6
class ClassA
{
public delegate void WriteLog(string msg);
private WriteLog m_WriteLogDelegate;

public ClassA(WriteLog writelog)
{
    m_WriteLogDelegate =  writelog;
    Thread thread = new Thread(new ThreadStart(Search));
    thread.Start();
}

public void Search()
{
    /* ... */
    m_WriteLogDelegate("msg");
    /* ... */
}

}

class classB
{
        private ClassA m_classA;

        protected void WriteLogCallBack(string msg)
        {
            // prints msg
            /* ... */
        }

        public classB()
        {
            m_classA = new ClassA(new WriteLog(WriteLogCallBack));
        }

        public void test1()
        {
            Thread thread = new Thread(new ThreadStart(Run));
            thread.Start();
        }

        public void test2()
        {
            m_classA.Search();
        }

        public void Run()
        {
            while(true)
            {
                /* ... */
                m_classA.Search();
                /* ... */
                Thread.Sleep(1000);
            }
        }
}

为什么以下代码
ClassB b = new ClassB();
b.test2() 

打印“msg”和这个

ClassB b = new ClassB();
b.test1() 

没有打印任何内容吗?


我在 test1() 函数中看到了一个线程... - BoltClock
1
测试2的代码在哪里?你传递给线程构造函数的Run方法的代码呢? - LoSciamano
1
向下滚动代码,它就在那里。 - Katie Kilian
1
ClassA 的构造函数长什么样? - Steve Guidi
3个回答

6
您的程序可能会退出,导致线程被终止(或者在线程有时间开始之前)。就像您明确地创建了一个线程一样,您需要明确地等待线程完成! 您需要使用Thread.Join或其他方法来使主程序等待线程完成。
一个可能的选项是使用Thread.Join:
public Thread test2()
{
    ...
    return thread;
}

...

b.test2().Join(); // wait for test2 to complete

另一种选项是使用 ManualResetEvent
class classB
{
    private ManualResetEvent mre = new ManualResetEvent(false);

    ...

    private void Run()
    {
        ...

        this.mre.Set(); // we completed our task
    }

    public void Wait();
    {
        this.mre.WaitOne();
    }

那么你调用 b.test2() 的代码如下:

b.test2();
b.Wait();

很奇怪——当我在程序中没有使用Join()时,打印线程会一直运行,程序永远不会终止。我本来期望操作系统至少会切换到主线程一次,导致程序终止。 - Steve Guidi
@Steve:这将取决于程序是如何运行的,是在控制台/图形用户界面(GUI)中。 - user7116
我忘了提到 ClassA.Search 也在一个单独的线程中运行。这可能会导致这个问题吗? - Caner
@LAS_VEGAS,我会遵循“正如你显式地创建线程一样,你需要显式地等待线程完成”的原则。你需要确保你的程序在所有线程的生命周期内都能够运行,包括从线程中派生出来的线程。 - user7116

2

你的代码对我来说可以使用,虽然我不得不填补从帖子中省略的部分。除非我做了与您不同的事情,否则问题必须出在其他地方。

下面的代码在控制台应用程序中正常工作:我每隔1秒钟看到“Test”被打印出来。

internal class ClassA
{
    public WriteLog Log { get; set; }

    public ClassA(WriteLog writeLog)
    {
        Log = writeLog;
    }

    public void Search()
    {
        Log.Print("Test");
    }
}

class classB
{
    private ClassA m_classA;

    protected void WriteLogCallBack(string msg)
    {
        // prints msg
        /* ... */
        Console.WriteLine(msg);
    }

    public classB()
    {
        m_classA = new ClassA(new WriteLog(WriteLogCallBack));
    }



    public void test1()
    {
        Thread thread = new Thread(new ThreadStart(Run));
        thread.Start();
    }

    public void test2()
    {
        m_classA.Search();
    }

    public void Run()
    {
        while (true)
        {
            /* ... */
            m_classA.Search();
            /* ... */
            Thread.Sleep(1000);
        }
    }
}

internal class WriteLog
{
    private Action<string> Callback { get; set; }

    public WriteLog(Action<string> writeLogCallBack)
    {
        Callback = writeLogCallBack;
    }

    public void Print(string msg)
    {
        Callback(msg);
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        classB b = new classB();
        b.test1();
        }
}

我忘了提到ClassA.Search也在一个单独的线程中运行。这可能会导致这个问题吗? - Caner
@LAS 不确定。如果您发布Search()的实现,可能会有所帮助。 - Adam Lear

1
在什么情况下调用了b.test1()方法?如果它是一个控制台应用程序,并且在调用b.test1()之后的下一步是终止程序,那么由b.test1()创建的线程可能永远不会在程序终止之前执行。 您需要等待足够的时间来构建新线程(昂贵的操作)并安排执行。 "多线程"和"并发"并不意味着瞬间完成。它们意味着在大量工作中每单位时间内完成更多的工作。
为了降低后台线程操作的成本,请考虑使用ThreadPool.QueueUserWorkItem()替换new Thread()以利用现有的工作池线程。这将节省时间和内存。
此外,请仔细考虑将要推送到后台线程的工作是否真的值得额外的线程开销。如果写入日志可能会在文件I/O或网络I/O上阻塞,那么在后台线程中执行可能是合适的,但也有其他技术可以执行异步I/O而无需创建新线程。
考虑到频率。最好启动一个后台线程来监听队列并大部分时间处于睡眠状态,然后让主线程将消息推送到队列中,而不是每次想输出几个字符的文本时启动一个新线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接