In multi-threading that clause
if(!p_inst)
{
    p_inst = new Singleton();
}
is actually 3 separate actions.  You are getting the value of p_inst, setting the value of p_inst and writing the value of p_inst.  So get-set-write means that you need to put a lock around p_inst otherwise you can have 2 threads which create a Singleton value that each thread uses.
Here is how you can view the issue, assume that your Singleton has a mutable field val:
thread A -> p_inst is NULL
    thread B -> p_inst is NULL
       thread A -> set to Singleton (1)
           thread B -> set to Singleton (2)
              thread C -> p_inst is Singleton (2)
                  thread A -> set val to 4
                      thread B -> set val to 6
                         thread C -> get val (it's 6)
                             thread A -> get val (it's 4!!)
You see?  There's 2 copies of a Singleton floating about, neither of which knows about the other.  The third thread which checks on the Singleton is only going to see the last assignment.  But with locking, you can prevent multiple assignment and these types of problems.